DAW JSON Link
Loading...
Searching...
No Matches
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
12
24
25#include <daw/daw_algorithm.h>
26#include <daw/daw_attributes.h>
27#include <daw/daw_move.h>
28#include <daw/daw_utility.h>
29
30#include <cassert>
31#include <cstddef>
32#include <daw/stdinc/tuple_traits.h>
33#include <optional>
34#include <string_view>
35
36namespace daw::json {
37 inline namespace DAW_JSON_VER {
40 template<json_options_t PolicyFlags = json_details::default_policy_flag,
41 typename Allocator = json_details::NoAllocator>
49
50 template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
51 constexpr decltype( auto )
53 static_assert(
54 Idx < 2,
55 "Invalid index. Valid values are 0 for name, and 1 for value" );
56 if constexpr( Idx == 0 ) {
57 return parse_state.name;
58 } else {
59 return parse_state.value;
60 }
61 }
62
63 template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
64 constexpr decltype( auto )
66 static_assert(
67 Idx < 2,
68 "Invalid index. Valid values are 0 for name, and 1 for value" );
69 if constexpr( Idx == 0 ) {
70 return parse_state.name;
71 } else {
72 return parse_state.value;
73 }
74 }
75
76 template<std::size_t Idx, json_options_t PolicyFlags, typename Allocator>
77 constexpr decltype( auto )
79 static_assert(
80 Idx < 2,
81 "Invalid index. Valid values are 0 for name, and 1 for value" );
82 if constexpr( Idx == 0 ) {
83 return std::move( parse_state.name );
84 } else {
85 return std::move( parse_state.value );
86 }
87 }
88 } // namespace DAW_JSON_VER
89} // namespace daw::json
90
91namespace std {
92 template<daw::json::json_options_t PolicyFlags, typename Allocator>
93 class tuple_element<0, daw::json::basic_json_pair<PolicyFlags, Allocator>> {
94 public:
95 using type = std::optional<std::string_view>;
96 };
97
98 template<daw::json::json_options_t PolicyFlags, typename Allocator>
99 class tuple_element<1, daw::json::basic_json_pair<PolicyFlags, Allocator>> {
100 public:
102 };
103
104 template<daw::json::json_options_t PolicyFlags, typename Allocator>
105 inline constexpr std::size_t
106 tuple_size_v<daw::json::basic_json_pair<PolicyFlags, Allocator>> = 2;
107
108 template<daw::json::json_options_t PolicyFlags, typename Allocator>
109 class tuple_size<daw::json::basic_json_pair<PolicyFlags, Allocator>> {
110 public:
111 static constexpr std::size_t value = 2;
112 };
113} // namespace std
114
115namespace daw::json {
116 inline namespace DAW_JSON_VER {
120 template<json_options_t PolicyFlags = json_details::default_policy_flag,
121 typename Allocator = json_details::NoAllocator>
123 using key_type = std::string_view;
125
129 using pointer = json_details::arrow_proxy<value_type>;
130 using difference_type = std::ptrdiff_t;
131 using iterator_category = std::forward_iterator_tag;
134
135 private:
137 ParseState m_state{ };
138
139 public:
140 explicit basic_json_value_iterator( ) = default;
141
142 explicit constexpr basic_json_value_iterator(
143 parse_policy const &parse_state )
144 : m_state( parse_state ) {}
145
146 explicit basic_json_value_iterator( daw::string_view json_doc )
147 : m_state( std::data( json_doc ), daw::data_end( json_doc ) ) {}
148
149 explicit basic_json_value_iterator( daw::string_view json_doc,
150 Allocator const &alloc )
151 : m_state( std::data( json_doc ), daw::data_end( json_doc ),
152 std::data( json_doc ), daw::data_end( json_doc ), alloc ) {}
153
156 : m_state( jv.get_raw_state( ) ) {}
157
160 [[nodiscard]] constexpr std::optional<std::string_view> name( ) const {
161 if( is_array( ) ) {
162 return { };
163 }
164 auto parse_state = m_state;
165 auto result = json_details::parse_name( parse_state );
166 return std::string_view( std::data( result ), std::size( result ) );
167 }
168
172 [[nodiscard]] constexpr basic_json_value<PolicyFlags, Allocator>
173 value( ) const {
174 if( is_array( ) ) {
175 return ParseState( m_state );
176 }
177 auto parse_state = m_state;
178 (void)json_details::parse_name( parse_state );
179 return ParseState( parse_state.first,
180 parse_state.last,
181 parse_state.first,
182 parse_state.last,
183 parse_state.get_allocator( ) );
184 }
185
188 [[nodiscard]] constexpr basic_json_pair<PolicyFlags, Allocator>
190 if( is_array( ) ) {
191 return { { },
192 basic_json_value( ParseState( m_state.first,
193 m_state.last,
194 m_state.first,
195 m_state.last,
196 m_state.get_allocator( ) ) ) };
197 }
198 auto parse_state = m_state;
199 auto name = json_details::parse_name( parse_state );
200 return {
201 std::string_view( std::data( name ), std::size( name ) ),
202 basic_json_value( ParseState( parse_state.first,
203 parse_state.last,
204 parse_state.first,
205 parse_state.last,
206 parse_state.get_allocator( ) ) ) };
207 }
208
213 [[nodiscard]] constexpr pointer operator->( ) {
214 return { operator*( ) };
215 }
216
220 if( good( ) ) {
221 if( is_class( ) ) {
222 (void)json_details::parse_name( m_state );
223 }
224 (void)json_details::skip_value( m_state );
225 m_state.move_next_member_or_end( );
226 }
227 return *this;
228 }
229
231 constexpr void operator++( int ) & {
232 operator++( );
233 }
234
237 [[nodiscard]] constexpr bool is_array( ) const {
238 return *m_state.class_first == '[';
239 }
240
243 [[nodiscard]] constexpr bool is_class( ) const {
244 return *m_state.class_first == '{';
245 }
246
249 [[nodiscard]] constexpr bool good( ) const {
250 if( m_state.is_null( ) or not m_state.has_more( ) ) {
251 return false;
252 }
253 switch( m_state.front( ) ) {
254 case '[':
255 case '{':
256 case '"':
257 case '-':
258 case '0':
259 case '1':
260 case '2':
261 case '3':
262 case '4':
263 case '5':
264 case '6':
265 case '7':
266 case '8':
267 case '9':
268 case 't':
269 case 'f':
270 case 'n':
271 return true;
272 case '}':
273 case ']':
274 return false;
275 default:
276 DAW_UNLIKELY_BRANCH
277 daw_json_error( true, ErrorReason::ExpectedTokenNotFound, m_state );
278 }
279 }
280
283 [[nodiscard]] constexpr explicit operator bool( ) const {
284 return good( );
285 }
286
289 [[nodiscard]] constexpr parse_policy const &get_raw_state( ) const {
290 return m_state;
291 }
292
296 template<json_options_t P, typename A>
297 [[nodiscard]] constexpr bool
299 if( good( ) ) {
300 if( rhs.good( ) ) {
301 return m_state.first == rhs.m_state.first;
302 }
303 return false;
304 }
305 return not rhs.good( );
306 }
307
311 template<json_options_t P, typename A>
312 [[nodiscard]] constexpr bool
314 return not operator==( rhs );
315 }
316 };
317
318 template<json_options_t PolicyFlags, typename Allocator>
322
323 basic_json_value_iterator( daw::string_view )
325
326 template<typename Allocator>
327 basic_json_value_iterator( daw::string_view, Allocator const & )
328 -> basic_json_value_iterator<daw::json::json_details::default_policy_flag,
329 Allocator>;
330
331 template<json_options_t PolicyFlags, typename Allocator>
335
338 template<json_options_t PolicyFlags = json_details::default_policy_flag,
339 typename Allocator = json_details::NoAllocator>
344
345 [[nodiscard]] constexpr iterator begin( ) {
346 return first;
347 }
348 [[nodiscard]] constexpr iterator end( ) {
349 return last;
350 }
351 };
352
353 template<json_options_t PolicyFlags, typename Allocator>
358
362 template<json_options_t PolicyFlags, typename Allocator>
366 ParseState m_parse_state{ };
369 using size_type = std::size_t;
370 using difference_type = std::ptrdiff_t;
371
372 basic_json_value( ) = default;
373
376 template<json_options_t P, typename A>
377 explicit constexpr basic_json_value( BasicParsePolicy<P, A> parse_state )
378 : m_parse_state( std::move( parse_state ) ) {
379 // Ensure we are at the actual value.
380 m_parse_state.trim_left( );
381 }
382
384 explicit constexpr basic_json_value( daw::string_view sv )
385 : m_parse_state( std::data( sv ), daw::data_end( sv ) ) {
386 m_parse_state.trim_left( );
387 }
388
390 explicit constexpr basic_json_value( char const *first DAW_LIFETIME_BOUND,
391 std::size_t sz )
392 : m_parse_state( first, first + static_cast<std::ptrdiff_t>( sz ) ) {
393 m_parse_state.trim_left( );
394 }
395
397 explicit constexpr basic_json_value( char const *first DAW_LIFETIME_BOUND,
398 char const *last )
399 : m_parse_state( first, last ) {
400 m_parse_state.trim_left( );
401 }
402
405 [[nodiscard]] constexpr ParseState get_raw_state( ) const {
406 return m_parse_state;
407 }
408
409 [[nodiscard]] constexpr std::string_view get_raw_json_document( ) const {
410 return std::string_view( m_parse_state.first, m_parse_state.size( ) );
411 }
412
416 [[nodiscard]] constexpr iterator begin( ) const {
417 auto parse_state = ParseState( m_parse_state.first,
418 m_parse_state.last,
419 m_parse_state.first,
420 m_parse_state.last,
421 m_parse_state.get_allocator( ) );
422 parse_state.remove_prefix( );
423 parse_state.trim_left( );
424 return iterator( parse_state );
425 }
426
429 [[nodiscard]] constexpr iterator end( ) const {
430 return iterator( );
431 }
432
437 [[nodiscard]] constexpr basic_json_value
438 find_class_member( daw::string_view name ) const {
439 if( type( ) != JsonBaseParseTypes::Class ) {
440 return basic_json_value{ };
441 }
442 bool const has_escape = name.contains( '\\' );
443 auto pos = [&] {
444 if( has_escape ) {
445 return daw::algorithm::find_if(
446 begin( ), end( ), [name]( auto const &jp ) {
447 assert( jp.name );
448 auto f0 = std::data( name );
449 auto const l0 = daw::data_end( name );
450 auto f1 = std::data( *jp.name );
451 auto const l1 = daw::data_end( *jp.name );
452 while( f0 != l0 and f1 != l1 ) {
453 if( *f0 == '\\' ) {
454 ++f0;
455 continue;
456 }
457 if( *f0 != *f1 ) {
458 return false;
459 }
460 ++f0;
461 ++f1;
462 }
463 return f0 == l0 and f1 == l1;
464 } );
465 } else {
466 return daw::algorithm::find_if(
467 begin( ), end( ), [name]( auto const &jp ) {
468 assert( jp.name );
469 return jp.name == name;
470 } );
471 }
472 }( );
473
474 if( pos == end( ) ) {
475 return basic_json_value( );
476 }
477 return ( *pos ).value;
478 }
479
482 [[nodiscard]] constexpr basic_json_value
483 find_member( daw::string_view json_path ) const {
484 auto jv = *this;
485 while( not json_path.empty( ) and jv ) {
486 auto member = [&] {
487 if( json_path.front( ) == '[' ) {
488 return json_path.pop_front_until( ']' );
489 }
490 return json_path.pop_front_until( escaped_any_of<'.', '['>{ },
491 nodiscard );
492 }( );
493 if( not json_path.empty( ) and json_path.front( ) == '.' ) {
494 json_path.remove_prefix( );
495 }
496 if( member.front( ) == '[' ) {
497 member.remove_prefix( );
498 auto index_ps =
500 std::data( member ), daw::data_end( member ) )
501 .with_allocator( m_parse_state.get_allocator( ) );
502 auto const index =
503 json_details::unsigned_parser<std::size_t,
504 options::JsonRangeCheck::Never,
505 true>( index_ps );
506
507 jv = jv.find_element( index );
508 if( not json_path.empty( ) and json_path.front( ) == '.' ) {
509 json_path.remove_prefix( );
510 }
511 continue;
512 }
513 jv = jv.find_class_member( member );
514 }
515 return jv;
516 }
517
520 template<typename Result>
521 [[nodiscard]] constexpr auto as( ) const {
522 using result_t = json_details::json_deduced_type<Result>;
523 auto state = m_parse_state;
524 return json_details::
525 parse_value<result_t, false, result_t::expected_type>( state );
526 }
527
528 template<typename Result>
529 [[nodiscard]] explicit operator Result( ) const {
530 return as<Result>( );
531 }
532
537 [[nodiscard]] constexpr basic_json_value
538 operator[]( daw::string_view json_path ) const {
539 return find_member( json_path );
540 }
541
545 [[nodiscard]] constexpr basic_json_value
546 find_element( std::size_t index ) const {
547 auto first = begin( );
548 auto const last = end( );
549 while( nsc_and( index > 0, first != last ) ) {
550 --index;
551 ++first;
552 }
553 if( index == 0 ) {
554 return ( *first ).value;
555 }
556 return basic_json_value( );
557 }
558
561 [[nodiscard]] constexpr basic_json_value
562 find_array_element( std::size_t index ) const {
563 assert( type( ) == JsonBaseParseTypes::Array );
564 return find_element( index );
565 }
566
570 [[nodiscard]] constexpr basic_json_value
571 operator[]( std::size_t index ) const {
572 return find_element( index );
573 }
574
578 [[nodiscard]] constexpr JsonBaseParseTypes type( ) const {
579 if( m_parse_state.empty( ) ) {
580 return JsonBaseParseTypes::None;
581 }
582 switch( m_parse_state.front( ) ) {
583 case '"':
584 return JsonBaseParseTypes::String;
585 case '{':
586 return JsonBaseParseTypes::Class;
587 case '[':
588 return JsonBaseParseTypes::Array;
589 case '-':
590 case '0':
591 case '1':
592 case '2':
593 case '3':
594 case '4':
595 case '5':
596 case '6':
597 case '7':
598 case '8':
599 case '9':
600 return JsonBaseParseTypes::Number;
601 case 't':
602 if constexpr( not ParseState::is_unchecked_input ) {
603 if( m_parse_state.starts_with( "true" ) ) {
604 return JsonBaseParseTypes::Bool;
605 }
606 return JsonBaseParseTypes::None;
607 } else {
608 return JsonBaseParseTypes::Bool;
609 }
610 case 'f':
611 if constexpr( not ParseState::is_unchecked_input ) {
612 if( m_parse_state.starts_with( "false" ) ) {
613 return JsonBaseParseTypes::Bool;
614 }
615 return JsonBaseParseTypes::None;
616 } else {
617 return JsonBaseParseTypes::Bool;
618 }
619 case 'n':
620 daw_json_assert_weak( m_parse_state.starts_with( "null" ),
621 ErrorReason::InvalidNull,
622 m_parse_state );
623 return JsonBaseParseTypes::Null;
624 }
625 return JsonBaseParseTypes::None;
626 }
627
630 [[nodiscard]] constexpr ParseState get_state( ) const {
631 auto parse_state = m_parse_state;
632 auto result = json_details::skip_value( parse_state );
633 if( is_string( ) ) {
634 --result.first;
635 ++result.last;
636 }
637 return result;
638 }
639
643 [[nodiscard]] constexpr std::string_view get_string_view( ) const {
644 auto parse_state = m_parse_state;
645 auto result = json_details::skip_value( parse_state );
646 return { std::data( result ), std::size( result ) };
647 }
648
652 template<typename Alloc = std::allocator<char>,
653 typename Traits = std::char_traits<char>>
654 [[nodiscard]] std::basic_string<char, Traits, Alloc>
655 get_string( Alloc const &alloc = Alloc( ) ) const {
656 auto parse_state = m_parse_state;
657 auto result = json_details::skip_value( parse_state );
658 return { std::data( result ), std::size( result ), alloc };
659 }
660
663 [[nodiscard]] constexpr bool is_null( ) const {
664 return type( ) == JsonBaseParseTypes::Null;
665 }
666
669 [[nodiscard]] constexpr bool is_class( ) const {
670 return type( ) == JsonBaseParseTypes::Class;
671 }
672
675 [[nodiscard]] constexpr bool is_array( ) const {
676 return type( ) == JsonBaseParseTypes::Array;
677 }
678
681 [[nodiscard]] constexpr bool is_number( ) const {
682 return type( ) == JsonBaseParseTypes::Number;
683 }
684
687 [[nodiscard]] constexpr bool is_string( ) const {
688 return type( ) == JsonBaseParseTypes::String;
689 }
690
693 [[nodiscard]] constexpr bool is_bool( ) const {
694 return type( ) == JsonBaseParseTypes::Bool;
695 }
696
701 [[nodiscard]] constexpr bool is_unknown( ) const {
702 return type( ) == JsonBaseParseTypes::None;
703 }
704
706 template<json_options_t P, typename A>
707 [[nodiscard]] constexpr
708 operator basic_json_value<P, A>( ) const noexcept {
709 auto new_range =
710 BasicParsePolicy<P, A>( m_parse_state.first, m_parse_state.last );
711 new_range.class_first = m_parse_state.class_first;
712 new_range.class_last = m_parse_state.class_last;
713 return basic_json_value<P, A>( std::move( new_range ) );
714 }
715
717 [[nodiscard]] explicit constexpr operator bool( ) const {
718 return type( ) != JsonBaseParseTypes::None;
719 }
720 };
721
722 template<json_options_t PolicyFlags, typename Allocator>
725
726 basic_json_value( daw::string_view ) -> basic_json_value<>;
727
728 basic_json_value( char const *first, std::size_t sz ) -> basic_json_value<>;
729
730 basic_json_value( char const *first, char const *last )
732
733 template<typename Result, json_options_t PolicyFlags, typename Allocator>
734 [[nodiscard]] constexpr Result
736 return jv.template as<Result>( );
737 }
738
739 namespace json_details {
740 // Will be specialized
741 template<typename>
742 inline constexpr bool is_json_value = false;
743
744 template<json_options_t PolicyFlags, typename Allocator>
745 inline constexpr bool
746 is_json_value<basic_json_value<PolicyFlags, Allocator>> = true;
747
748 template<json_options_t PolicyFlags, typename Allocator>
749 inline constexpr bool
750 is_string_view_like_v<basic_json_value<PolicyFlags, Allocator>> = false;
751 } // namespace json_details
752 } // namespace DAW_JSON_VER
753} // 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(bool b, ErrorReason reason)
daw::conditional_t< ParsePolicy::is_default_parse_policy, DefaultParsePolicy, ParsePolicy > TryDefaultParsePolicy
constexpr Result as(basic_json_value< PolicyFlags, Allocator > const &jv)
constexpr decltype(auto) get(basic_json_pair< PolicyFlags, Allocator > const &parse_state)
Customization point traits.
Handles the bounds and policy items for parsing execution and comments.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > ParseState
a rudimentary range object for holding basic_json_value_iterator
Iterator for iterating over arbitrary JSON members and array elements.
constexpr pointer operator->()
Return an arrow_proxy object containing the result of operator* Should not use this method unless you...
constexpr std::optional< std::string_view > name() const
Name of member.
constexpr parse_policy const & get_raw_state() const
Get access to the internal state. Should not be used as part of public API.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > parse_policy
constexpr basic_json_value< PolicyFlags, Allocator > value() const
Get the value currently being referenced.
constexpr basic_json_value_iterator & operator++()
Move the parser to the next value.
constexpr bool is_class() const
Is the value this iterator iterates over an class.
constexpr bool is_array() const
Is the value this iterator iterates over an array.
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 basic_json_pair< PolicyFlags, Allocator > operator*()
Get the name/value pair of the currently referenced element.
constexpr bool operator!=(basic_json_value_iterator< P, A > const &rhs) const
Check if rhs is not equivalent to self.
A non-owning container for arbitrary JSON values that allows movement/iteration through.
constexpr bool is_null() const
Is the JSON value a null literal.
constexpr bool is_class() const
Is the JSON value a class.
constexpr std::string_view get_string_view() const
Construct a string range of the current value. Strings start inside the quotes.
constexpr bool is_bool() const
Is the JSON value a boolean.
constexpr iterator end() const
End of range over class/arrays members/items.
constexpr basic_json_value operator[](daw::string_view json_path) const
Query the current class for a named member.
TryDefaultParsePolicy< BasicParsePolicy< PolicyFlags, Allocator > > ParseState
constexpr ParseState get_state() const
Construct a string range of the current value.
constexpr basic_json_value(daw::string_view sv)
Construct from string_view.
constexpr basic_json_value(char const *first DAW_LIFETIME_BOUND, std::size_t sz)
Construct from char const *, std::size_t.
constexpr auto as() const
Parse the current json member as a Result. The Result type must be supported or mapped via a json_dat...
constexpr basic_json_value find_array_element(std::size_t index) const
Find the nth element of the current json array.
constexpr iterator begin() const
Get the first member/item.
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 bool is_array() const
Is the JSON value a array.
constexpr bool is_string() const
Is the JSON value a string.
constexpr bool is_number() const
Is the JSON value a number literal.
constexpr ParseState get_raw_state() const
Get a copy of the underlying parse state.
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(BasicParsePolicy< P, A > parse_state)
Construct from IteratorRange.
constexpr bool is_unknown() const
Is the JSON data unrecognizable. JSON members will start with one of ",[,{,0,1,2,3,...
constexpr basic_json_value(char const *first DAW_LIFETIME_BOUND, char const *last)
Construct from char const *, char const *.
constexpr JsonBaseParseTypes type() const
Get the type of JSON value.
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:20