DAW JSON Link
daw_json_value.h
Go to the documentation of this file.
1 // Copyright (c) Darrell Wright
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //
6 // Official repository: https://github.com/beached/daw_json_link
7 //
8 
9 #pragma once
10 
11 #include "version.h"
12 
13 #include "daw_json_arrow_proxy.h"
14 #include "daw_json_assert.h"
15 #include "daw_json_parse_name.h"
16 #include "daw_json_parse_policy.h"
19 #include "daw_json_skip.h"
20 #include "daw_json_traits.h"
21 #include "daw_json_value_fwd.h"
23 
24 #include <daw/daw_algorithm.h>
25 #include <daw/daw_move.h>
26 #include <daw/daw_utility.h>
27 
28 #include <cassert>
29 #include <cstddef>
30 #include <daw/stdinc/tuple_traits.h>
31 #include <optional>
32 #include <string_view>
33 
34 namespace daw::json {
35  inline namespace DAW_JSON_VER {
38  template<json_options_t PolicyFlags = json_details::default_policy_flag,
39  typename Allocator = json_details::NoAllocator>
40  struct basic_json_pair {
41  using ParseState =
43 
44  std::optional<std::string_view> name;
46  };
47 
48  template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
49  constexpr decltype( auto )
50  get( basic_json_pair<PolicyFlags, Allocator> const &parse_state ) {
51  static_assert(
52  Idx < 2,
53  "Invalid index. Valid values are 0 for name, and 1 for value" );
54  if constexpr( Idx == 0 ) {
55  return parse_state.name;
56  } else {
57  return parse_state.value;
58  }
59  }
60 
61  template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
62  constexpr decltype( auto )
63  get( basic_json_pair<PolicyFlags, Allocator> &parse_state ) {
64  static_assert(
65  Idx < 2,
66  "Invalid index. Valid values are 0 for name, and 1 for value" );
67  if constexpr( Idx == 0 ) {
68  return parse_state.name;
69  } else {
70  return parse_state.value;
71  }
72  }
73 
74  template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
75  constexpr decltype( auto )
76  get( basic_json_pair<PolicyFlags, Allocator> &&parse_state ) {
77  static_assert(
78  Idx < 2,
79  "Invalid index. Valid values are 0 for name, and 1 for value" );
80  if constexpr( Idx == 0 ) {
81  return std::move( parse_state.name );
82  } else {
83  return std::move( parse_state.value );
84  }
85  }
86  } // namespace DAW_JSON_VER
87 } // namespace daw::json
88 
89 namespace std {
90  template<daw::json::json_options_t PolicyFlags, typename Allocator>
91  class tuple_element<0, daw::json::basic_json_pair<PolicyFlags, Allocator>> {
92  public:
93  using type = std::optional<std::string_view>;
94  };
95 
96  template<daw::json::json_options_t PolicyFlags, typename Allocator>
97  class tuple_element<1, daw::json::basic_json_pair<PolicyFlags, Allocator>> {
98  public:
100  };
101 
102  template<daw::json::json_options_t PolicyFlags, typename Allocator>
103  class tuple_size<daw::json::basic_json_pair<PolicyFlags, Allocator>>
104  : public std::integral_constant<std::size_t, 2> {};
105 } // namespace std
106 
107 namespace daw::json {
108  inline namespace DAW_JSON_VER {
112  template<json_options_t PolicyFlags = json_details::default_policy_flag,
113  typename Allocator = json_details::NoAllocator>
115  using key_type = std::string_view;
117 
121  using pointer = json_details::arrow_proxy<value_type>;
122  using difference_type = std::ptrdiff_t;
123  using iterator_category = std::forward_iterator_tag;
124  using parse_policy =
126 
127  private:
129  ParseState m_state{ };
130 
131  public:
132  explicit basic_json_value_iterator( ) = default;
133 
134  explicit constexpr basic_json_value_iterator(
135  parse_policy const &parse_state )
136  : m_state( parse_state ) {}
137 
138  explicit basic_json_value_iterator( daw::string_view json_doc )
139  : m_state( std::data( json_doc ), daw::data_end( json_doc ) ) {}
140 
141  explicit basic_json_value_iterator( daw::string_view json_doc,
142  Allocator const &alloc )
143  : m_state( std::data( json_doc ), daw::data_end( json_doc ),
144  std::data( json_doc ), daw::data_end( json_doc ), alloc ) {}
145 
148  : m_state( jv.get_raw_state( ) ) {}
149 
152  [[nodiscard]] constexpr std::optional<std::string_view> name( ) const {
153  if( is_array( ) ) {
154  return { };
155  }
156  auto parse_state = m_state;
157  auto result = json_details::parse_name( parse_state );
158  return std::string_view( std::data( result ), std::size( result ) );
159  }
160 
164  [[nodiscard]] constexpr basic_json_value<PolicyFlags, Allocator>
165  value( ) const {
166  if( is_array( ) ) {
167  return ParseState( m_state );
168  }
169  auto parse_state = m_state;
170  (void)json_details::parse_name( parse_state );
171  return ParseState( parse_state.first, parse_state.last,
172  parse_state.first, parse_state.last,
173  parse_state.get_allocator( ) );
174  }
175 
178  [[nodiscard]] constexpr basic_json_pair<PolicyFlags, Allocator>
180  if( is_array( ) ) {
181  return { { },
182  basic_json_value( ParseState( m_state.first, m_state.last,
183  m_state.first, m_state.last,
184  m_state.get_allocator( ) ) ) };
185  }
186  auto parse_state = m_state;
187  auto name = json_details::parse_name( parse_state );
188  return { std::string_view( std::data( name ), std::size( name ) ),
190  parse_state.first, parse_state.last, parse_state.first,
191  parse_state.last, parse_state.get_allocator( ) ) ) };
192  }
193 
198  [[nodiscard]] constexpr pointer operator->( ) {
199  return { operator*( ) };
200  }
201 
204  constexpr basic_json_value_iterator &operator++( ) {
205  if( good( ) ) {
206  if( is_class( ) ) {
207  (void)json_details::parse_name( m_state );
208  }
209  (void)json_details::skip_value( m_state );
210  m_state.move_next_member_or_end( );
211  }
212  return *this;
213  }
214 
216  inline constexpr void operator++( int ) & {
217  operator++( );
218  }
219 
222  [[nodiscard]] constexpr bool is_array( ) const {
223  return *m_state.class_first == '[';
224  }
225 
228  [[nodiscard]] constexpr bool is_class( ) const {
229  return *m_state.class_first == '{';
230  }
231 
234  [[nodiscard]] constexpr bool good( ) const {
235  if( not m_state.has_more( ) or m_state.is_null( ) ) {
236  return false;
237  }
238  switch( m_state.front( ) ) {
239  case '[':
240  case '{':
241  case '"':
242  case '-':
243  case '0':
244  case '1':
245  case '2':
246  case '3':
247  case '4':
248  case '5':
249  case '6':
250  case '7':
251  case '8':
252  case '9':
253  case 't':
254  case 'f':
255  case 'n':
256  return true;
257  case '}':
258  case ']':
259  return false;
260  default:
261  DAW_UNLIKELY_BRANCH
262  daw_json_error( ErrorReason::ExpectedTokenNotFound, m_state );
263  }
264  }
265 
268  [[nodiscard]] constexpr explicit operator bool( ) const {
269  return good( );
270  }
271 
274  [[nodiscard]] constexpr parse_policy const &get_raw_state( ) const {
275  return m_state;
276  }
277 
281  template<json_options_t P, typename A>
282  [[nodiscard]] constexpr bool
284  if( good( ) ) {
285  if( rhs.good( ) ) {
286  return m_state.first == rhs.m_state.first;
287  }
288  return false;
289  }
290  return not rhs.good( );
291  }
292 
296  template<json_options_t P, typename A>
297  [[nodiscard]] constexpr bool
299  return not operator==( rhs );
300  }
301  };
302 
303  template<json_options_t PolicyFlags, typename Allocator>
307 
308  basic_json_value_iterator( daw::string_view )
310 
311  template<typename Allocator>
312  basic_json_value_iterator( daw::string_view, Allocator const & )
313  -> basic_json_value_iterator<daw::json::json_details::default_policy_flag,
314  Allocator>;
315 
316  template<json_options_t PolicyFlags, typename Allocator>
320 
323  template<json_options_t PolicyFlags = json_details::default_policy_flag,
324  typename Allocator = json_details::NoAllocator>
330 
331  [[nodiscard]] constexpr iterator begin( ) {
332  return first;
333  }
334  [[nodiscard]] constexpr iterator end( ) {
335  return last;
336  }
337  };
338 
339  template<json_options_t PolicyFlags, typename Allocator>
344 
348  template<json_options_t PolicyFlags, typename Allocator>
350  using ParseState =
352  ParseState m_parse_state{ };
353  using CharT = typename ParseState::CharT;
356  using size_type = std::size_t;
357  using difference_type = std::ptrdiff_t;
358 
359  basic_json_value( ) = default;
360 
363  template<json_options_t P, typename A>
364  explicit inline constexpr basic_json_value(
365  BasicParsePolicy<P, A> parse_state )
366  : m_parse_state( std::move( parse_state ) ) {
367  // Ensure we are at the actual value.
368  m_parse_state.trim_left( );
369  }
370 
372  explicit inline constexpr basic_json_value( daw::string_view sv )
373  : m_parse_state( std::data( sv ), daw::data_end( sv ) ) {
374  m_parse_state.trim_left( );
375  }
376 
378  explicit inline constexpr basic_json_value( CharT *first, std::size_t sz )
379  : m_parse_state( first, first + static_cast<std::ptrdiff_t>( sz ) ) {
380  m_parse_state.trim_left( );
381  }
382 
384  explicit inline constexpr basic_json_value( CharT *first, CharT *last )
385  : m_parse_state( first, last ) {
386  m_parse_state.trim_left( );
387  }
388 
391  [[nodiscard]] inline constexpr ParseState get_raw_state( ) const {
392  return m_parse_state;
393  }
394 
395  [[nodiscard]] inline constexpr std::string_view
397  return std::string_view( m_parse_state.first, m_parse_state.size( ) );
398  }
399 
403  [[nodiscard]] inline constexpr iterator begin( ) const {
404  auto parse_state = ParseState( m_parse_state.first, m_parse_state.last,
405  m_parse_state.first, m_parse_state.last,
406  m_parse_state.get_allocator( ) );
407  parse_state.remove_prefix( );
408  parse_state.trim_left( );
409  return iterator( parse_state );
410  }
411 
414  [[nodiscard]] inline constexpr iterator end( ) const {
415  return iterator( );
416  }
417 
422  [[nodiscard]] constexpr basic_json_value
423  find_class_member( daw::string_view name ) const {
424  if( type( ) != JsonBaseParseTypes::Class ) {
425  return basic_json_value{ };
426  }
427  bool const has_escape = name.contains( '\\' );
428  auto pos = [&] {
429  if( has_escape ) {
430  return daw::algorithm::find_if(
431  begin( ), end( ), [name]( auto const &jp ) {
432  assert( jp.name );
433  auto f0 = std::data( name );
434  auto const l0 = daw::data_end( name );
435  auto f1 = std::data( *jp.name );
436  auto const l1 = daw::data_end( *jp.name );
437  while( f0 != l0 and f1 != l1 ) {
438  if( *f0 == '\\' ) {
439  ++f0;
440  continue;
441  }
442  if( *f0 != *f1 ) {
443  return false;
444  }
445  ++f0;
446  ++f1;
447  }
448  return f0 == l0 and f1 == l1;
449  } );
450  } else {
451  return daw::algorithm::find_if( begin( ), end( ),
452  [name]( auto const &jp ) {
453  assert( jp.name );
454  return jp.name == name;
455  } );
456  }
457  }( );
458 
459  if( pos == end( ) ) {
460  return basic_json_value( );
461  }
462  return ( *pos ).value;
463  }
464 
467  [[nodiscard]] constexpr basic_json_value
468  find_member( daw::string_view json_path ) const {
469  auto jv = *this;
470  while( not json_path.empty( ) and jv ) {
471  auto member = [&] {
472  if( json_path.front( ) == '[' ) {
473  return json_path.pop_front_until( ']' );
474  }
475  return json_path.pop_front_until( escaped_any_of<'.', '['>{ },
476  nodiscard );
477  }( );
478  if( not json_path.empty( ) and json_path.front( ) == '.' ) {
479  json_path.remove_prefix( );
480  }
481  if( member.front( ) == '[' ) {
482  member.remove_prefix( );
483  auto index_ps =
485  std::data( member ), daw::data_end( member ) )
486  .with_allocator( m_parse_state.get_allocator( ) );
487  auto const index = json_details::unsigned_parser<
488  std::size_t, options::JsonRangeCheck::Never, true>(
489  constexpr_exec_tag{ }, index_ps );
490 
491  jv = jv.find_element( index );
492  if( not json_path.empty( ) and json_path.front( ) == '.' ) {
493  json_path.remove_prefix( );
494  }
495  continue;
496  }
497  jv = jv.find_class_member( member );
498  }
499  return jv;
500  }
501 
504  template<typename Result>
505  [[nodiscard]] constexpr auto as( ) const {
506  using result_t = json_details::json_deduced_type<Result>;
507  auto state = m_parse_state;
508  return json_details::parse_value<result_t, false,
509  result_t::expected_type>( state );
510  }
511 
512  template<typename Result>
513  [[nodiscard]] explicit operator Result( ) const {
514  return as<Result>( );
515  }
516 
521  [[nodiscard]] constexpr basic_json_value
522  operator[]( daw::string_view json_path ) const {
523  return find_member( json_path );
524  }
525 
529  [[nodiscard]] constexpr basic_json_value
530  find_element( std::size_t index ) const {
531  auto first = begin( );
532  auto const last = end( );
533  while( nsc_and( index > 0, first != last ) ) {
534  --index;
535  ++first;
536  }
537  if( index == 0 ) {
538  return ( *first ).value;
539  }
540  return basic_json_value( );
541  }
542 
545  [[nodiscard]] constexpr basic_json_value
546  find_array_element( std::size_t index ) const {
547  assert( type( ) == JsonBaseParseTypes::Array );
548  return find_element( index );
549  }
550 
554  [[nodiscard]] constexpr basic_json_value
555  operator[]( std::size_t index ) const {
556  return find_element( index );
557  }
558 
562  [[nodiscard]] constexpr JsonBaseParseTypes type( ) const {
563  if( not m_parse_state.has_more( ) ) {
564  return JsonBaseParseTypes::None;
565  }
566  switch( m_parse_state.front( ) ) {
567  case '"':
568  return JsonBaseParseTypes::String;
569  case '{':
570  return JsonBaseParseTypes::Class;
571  case '[':
572  return JsonBaseParseTypes::Array;
573  case '-':
574  case '0':
575  case '1':
576  case '2':
577  case '3':
578  case '4':
579  case '5':
580  case '6':
581  case '7':
582  case '8':
583  case '9':
584  return JsonBaseParseTypes::Number;
585  case 't':
586  if constexpr( not ParseState::is_unchecked_input ) {
587  if( m_parse_state.starts_with( "true" ) ) {
588  return JsonBaseParseTypes::Bool;
589  }
590  return JsonBaseParseTypes::None;
591  } else {
592  return JsonBaseParseTypes::Bool;
593  }
594  case 'f':
595  if constexpr( not ParseState::is_unchecked_input ) {
596  if( m_parse_state.starts_with( "false" ) ) {
597  return JsonBaseParseTypes::Bool;
598  }
599  return JsonBaseParseTypes::None;
600  } else {
601  return JsonBaseParseTypes::Bool;
602  }
603  case 'n':
604  daw_json_assert_weak( m_parse_state.starts_with( "null" ),
605  ErrorReason::InvalidNull, m_parse_state );
606  return JsonBaseParseTypes::Null;
607  }
608  return JsonBaseParseTypes::None;
609  }
610 
613  [[nodiscard]] constexpr ParseState get_state( ) const {
614  auto parse_state = m_parse_state;
615  auto result = json_details::skip_value( parse_state );
616  if( is_string( ) ) {
617  --result.first;
618  ++result.last;
619  }
620  return result;
621  }
622 
626  [[nodiscard]] constexpr std::string_view get_string_view( ) const {
627  auto parse_state = m_parse_state;
628  auto result = json_details::skip_value( parse_state );
629  return { std::data( result ), std::size( result ) };
630  }
631 
635  template<typename Alloc = std::allocator<char>,
636  typename Traits = std::char_traits<char>>
637  [[nodiscard]] std::basic_string<char, Traits, Alloc>
638  get_string( Alloc const &alloc = Alloc( ) ) const {
639  auto parse_state = m_parse_state;
640  auto result = json_details::skip_value( parse_state );
641  return { std::data( result ), std::size( result ), alloc };
642  }
643 
646  [[nodiscard]] constexpr bool is_null( ) const {
647  return type( ) == JsonBaseParseTypes::Null;
648  }
649 
652  [[nodiscard]] constexpr bool is_class( ) const {
653  return type( ) == JsonBaseParseTypes::Class;
654  }
655 
658  [[nodiscard]] constexpr bool is_array( ) const {
659  return type( ) == JsonBaseParseTypes::Array;
660  }
661 
664  [[nodiscard]] constexpr bool is_number( ) const {
665  return type( ) == JsonBaseParseTypes::Number;
666  }
667 
670  [[nodiscard]] inline constexpr bool is_string( ) const {
671  return type( ) == JsonBaseParseTypes::String;
672  }
673 
676  [[nodiscard]] constexpr bool is_bool( ) const {
677  return type( ) == JsonBaseParseTypes::Bool;
678  }
679 
684  [[nodiscard]] inline constexpr bool is_unknown( ) const {
685  return type( ) == JsonBaseParseTypes::None;
686  }
687 
689  template<json_options_t P, typename A>
690  [[nodiscard]] constexpr
691  operator basic_json_value<P, A>( ) const noexcept {
692  auto new_range =
693  BasicParsePolicy<P, A>( m_parse_state.first, m_parse_state.last );
694  new_range.class_first = m_parse_state.class_first;
695  new_range.class_last = m_parse_state.class_last;
696  return basic_json_value<P, A>( std::move( new_range ) );
697  }
698 
700  [[nodiscard]] explicit constexpr operator bool( ) const {
701  return type( ) != JsonBaseParseTypes::None;
702  }
703  };
704 
705  template<json_options_t PolicyFlags, typename Allocator>
708 
709  basic_json_value( daw::string_view ) -> basic_json_value<>;
710 
711  basic_json_value( char const *first, std::size_t sz ) -> basic_json_value<>;
712 
713  basic_json_value( char const *first,
714  char const *last ) -> basic_json_value<>;
715 
716  template<typename Result, json_options_t PolicyFlags, typename Allocator>
717  [[nodiscard]] constexpr Result
719  return jv.template as<Result>( );
720  }
721 
722  namespace json_details {
723  template<typename>
724  inline constexpr bool is_json_value = false;
725 
726  template<json_options_t PolicyFlags, typename Allocator>
727  inline constexpr bool
728  is_json_value<basic_json_value<PolicyFlags, Allocator>> = true;
729 
730  template<json_options_t PolicyFlags, typename Allocator>
731  inline constexpr bool
732  is_string_view_like_v<basic_json_value<PolicyFlags, Allocator>> = false;
733  } // namespace json_details
734  } // namespace DAW_JSON_VER
735 } // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Assert that Bool is true when in Checked Input mode If false pass rest of args to daw_json_error.
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
daw::conditional_t< ParsePolicy::is_default_parse_policy, DefaultParsePolicy, ParsePolicy > TryDefaultParsePolicy
basic_json_value_iterator(BasicParsePolicy< PolicyFlags, Allocator > const &) -> basic_json_value_iterator< PolicyFlags, Allocator >
constexpr decltype(auto) get(basic_json_pair< PolicyFlags, Allocator > const &parse_state)
constexpr Result as(basic_json_value< PolicyFlags, Allocator > const &jv)
basic_json_value_iterator_range(basic_json_value_iterator< PolicyFlags, Allocator >, basic_json_value_iterator< PolicyFlags, Allocator >) -> basic_json_value_iterator_range< PolicyFlags, Allocator >
basic_json_value(BasicParsePolicy< PolicyFlags, Allocator >) -> basic_json_value< PolicyFlags, Allocator >
Customization point traits.
Handles the bounds and policy items for parsing execution and comments.
A name/value pair of string_view/json_value.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > ParseState
a rudimentary range object for holding basic_json_value_iterator
typename BasicParsePolicy< PolicyFlags, Allocator >::CharT CharT
Iterator for iterating over arbitrary JSON members and array elements.
constexpr basic_json_pair< PolicyFlags, Allocator > operator*()
Get the name/value pair of the currently referenced element.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > parse_policy
constexpr bool operator==(basic_json_value_iterator< P, A > const &rhs) const
Check for equivalence with rhs iterator.
basic_json_value_iterator(daw::string_view json_doc, Allocator const &alloc)
basic_json_value_iterator(basic_json_value< PolicyFlags, Allocator > const &jv)
constexpr bool operator!=(basic_json_value_iterator< P, A > const &rhs) const
Check if rhs is not equivalent to self.
constexpr basic_json_value< PolicyFlags, Allocator > value() const
Get the value currently being referenced.
A non-owning container for arbitrary JSON values that allows movement/iteration through.
constexpr basic_json_value operator[](daw::string_view json_path) const
Query the current class for a named member.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > ParseState
std::basic_string< char, Traits, Alloc > get_string(Alloc const &alloc=Alloc()) const
Construct a string range of the current value. Strings start inside the quotes.
constexpr basic_json_value find_array_element(std::size_t index) const
Find the nth element of the current json array.
constexpr basic_json_value find_element(std::size_t index) const
Find the nth element/submember of the current json array or class.
constexpr basic_json_value find_member(daw::string_view json_path) const
find a class member/array element as specified by the json_path
constexpr basic_json_value operator[](std::size_t index) const
Find the nth element/submember of the current json array or class.
constexpr basic_json_value find_class_member(daw::string_view name) const
Query the current class for a named member.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25