DAW JSON Link
Loading...
Searching...
No Matches
daw_json_find_path.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
16
17#include <daw/algorithms/daw_algorithm_accumulate.h>
18#include <daw/algorithms/daw_algorithm_find.h>
19#include <daw/daw_cpp_feature_check.h>
20#include <daw/daw_move.h>
21#include <daw/iterator/daw_reverse_iterator.h>
22
23#include <optional>
24#include <string>
25#include <string_view>
26#include <vector>
27
28namespace daw::json {
29 inline namespace DAW_JSON_VER {
30 namespace json_details {
31 struct less {
33
34 template<typename T, typename U>
35 DAW_ATTRIB_INLINE DAW_JSON_CPP23_STATIC_CALL_OP constexpr bool
36 operator( )( T const &lhs, U const &rhs )
38 return lhs < rhs;
39 }
40
42 };
43 } // namespace json_details
44
45 class json_path_node;
46
47 [[nodiscard]] DAW_CPP20_CX_ALLOC std::vector<json_path_node>
48 find_json_path_stack_to( char const *parse_location,
49 char const *doc_start );
50
52 std::string_view m_name{ };
53 char const *m_value_start = nullptr;
54 long long m_index = -1;
55 JsonBaseParseTypes m_type = JsonBaseParseTypes::None;
56
57 friend std::vector<json_path_node>
58 DAW_CPP20_CX_ALLOC find_json_path_stack_to( char const *parse_location,
59 char const *doc_start );
60
61 json_path_node( ) = default;
62
63 constexpr json_path_node( JsonBaseParseTypes Type, std::string_view Name,
64 long long Index, char const *ValueStart )
65 : m_name( Name )
66 , m_value_start( ValueStart )
67 , m_index( Index )
68 , m_type( Type ) {}
69
70 public:
72 [[nodiscard]] constexpr JsonBaseParseTypes type( ) const {
73 return m_type;
74 }
75
77 [[nodiscard]] constexpr std::string_view name( ) const {
78 return m_name;
79 }
80
82 [[nodiscard]] constexpr long long index( ) const {
83 return m_index;
84 }
85
87 [[nodiscard]] constexpr char const *value_start( ) const {
88 return m_value_start;
89 }
90 };
91
95 [[nodiscard]] DAW_ATTRIB_NOINLINE DAW_CPP20_CX_ALLOC std::string
96 to_json_path_string( std::vector<json_path_node> const &path_stack ) {
97 return daw::algorithm::accumulate(
98 std::data( path_stack ),
99 daw::data_end( path_stack ),
100 std::string{ },
101 []( auto &&state, json_path_node const &sv )
103 if( sv.index( ) >= 0 ) {
104 state += '[';
105 state += std::to_string( sv.index( ) );
106 state += ']';
107 } else if( not sv.name( ).empty( ) ) {
108 state += '.';
109 state += std::string( sv.name( ) );
110 }
111 return DAW_FWD( state );
112 } );
113 }
114
119 [[nodiscard]] DAW_CPP20_CX_ALLOC std::vector<json_path_node>
120 find_json_path_stack_to( char const *parse_location,
121 char const *doc_start ) {
122 if( parse_location == nullptr or doc_start == nullptr ) {
123 return { };
124 }
125 if( json_details::less{ }( parse_location, doc_start ) ) {
126 return { };
127 }
128
129 struct handler_t {
130 char const *first;
131 char const *last;
132 std::vector<json_path_node> parse_stack{ };
133
134 // This is for when we throw after array/class end, but before the
135 // next value starts
136 std::optional<json_path_node> last_popped{ };
137 json_path_node state{ };
138
139 JsonBaseParseTypes child_of( ) {
140 if( parse_stack.empty( ) ) {
141 return JsonBaseParseTypes::None;
142 }
143 return parse_stack.back( ).type( );
144 }
145
146 [[nodiscard]] bool handle_on_value( json_pair jp ) {
147 if( auto const range = jp.value.get_raw_state( );
148 range.empty( ) or last <= std::data( range ) ) {
149 return false;
150 }
151 if( auto const t = child_of( ); t == JsonBaseParseTypes::Class ) {
152 state.m_name = *jp.name;
153 state.m_index = -1;
154 } else if( t == JsonBaseParseTypes::Array ) {
155 state.m_name = { };
156 state.m_index++;
157 } else {
158 state.m_name = { };
159 state.m_index = -1;
160 }
161 state.m_value_start = jp.value.get_raw_state( ).first;
162 state.m_type = jp.value.type( );
163 last_popped = std::nullopt;
164 return true;
165 }
166
167 [[nodiscard]] bool handle_on_array_start( json_value const & ) {
168 parse_stack.push_back( state );
169 state = { };
170 return true;
171 }
172
173 [[nodiscard]] bool handle_on_array_end( ) {
174 if( not parse_stack.empty( ) ) {
175 last_popped = parse_stack.back( );
176 state = parse_stack.back( );
177 parse_stack.pop_back( );
178 }
179 return true;
180 }
181
182 [[nodiscard]] bool handle_on_class_start( json_value const & ) {
183 parse_stack.push_back( state );
184 state = { };
185 return true;
186 }
187
188 [[nodiscard]] bool handle_on_class_end( ) {
189 if( not parse_stack.empty( ) ) {
190 last_popped = parse_stack.back( );
191 state = parse_stack.back( );
192 parse_stack.pop_back( );
193 }
194 return true;
195 }
196
197 [[nodiscard]] bool handle_on_number( json_value jv ) {
198 auto sv = std::string_view( );
199#if defined( DAW_USE_EXCEPTIONS )
200 try {
201#endif
202 sv = jv.get_string_view( );
203#if defined( DAW_USE_EXCEPTIONS )
204 } catch( json_exception const & ) {
205 parse_stack.push_back( state );
206 return false;
207 }
208#endif
209 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
210 parse_stack.push_back( state );
211 return false;
212 }
213 return true;
214 }
215
216 [[nodiscard]] bool handle_on_bool( json_value jv ) {
217 auto sv = std::string_view( );
218#if defined( DAW_USE_EXCEPTIONS )
219 try {
220#endif
221 sv = jv.get_string_view( );
222#if defined( DAW_USE_EXCEPTIONS )
223 } catch( json_exception const & ) {
224 parse_stack.push_back( state );
225 return false;
226 }
227#endif
228 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
229 parse_stack.push_back( state );
230 return false;
231 }
232 return true;
233 }
234
235 [[nodiscard]] bool handle_on_string( json_value jv ) {
236 auto sv = std::string_view( );
237#if defined( DAW_USE_EXCEPTIONS )
238 try {
239#endif
240 sv = jv.get_string_view( );
241#if defined( DAW_USE_EXCEPTIONS )
242 } catch( json_exception const & ) {
243 parse_stack.push_back( state );
244 return false;
245 }
246#endif
247 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
248 parse_stack.push_back( state );
249 return false;
250 }
251 return true;
252 }
253
254 [[nodiscard]] bool handle_on_null( json_value jv ) {
255 auto sv = std::string_view( );
256#if defined( DAW_USE_EXCEPTIONS )
257 try {
258#endif
259 sv = jv.get_string_view( );
260#if defined( DAW_USE_EXCEPTIONS )
261 } catch( json_exception const & ) {
262 parse_stack.push_back( state );
263 return false;
264 }
265#endif
266 if( std::data( sv ) <= last and last <= daw::data_end( sv ) ) {
267 parse_stack.push_back( state );
268 return false;
269 }
270 return true;
271 }
272 } handler{ doc_start, parse_location + 1 };
273
274#if defined( DAW_USE_EXCEPTIONS )
275 try {
276#endif
277 json_event_parser( doc_start, handler );
278#if defined( DAW_USE_EXCEPTIONS )
279 } catch( json_exception const & ) {
280 // Ignoring because we are only looking for the stack leading up to
281 // this and it may have come from an error
282 }
283#endif
284 if( handler.last_popped ) {
285 handler.parse_stack.push_back( *handler.last_popped );
286 }
287 return std::move( handler.parse_stack );
288 }
289
290 [[nodiscard]] DAW_CPP20_CX_ALLOC std::vector<json_path_node>
291 find_json_path_stack_to( json_exception const &jex,
292 char const *doc_start ) {
293 return find_json_path_stack_to( jex.parse_location( ), doc_start );
294 }
295
296 [[nodiscard]] DAW_CPP20_CX_ALLOC std::string
297 find_json_path_to( char const *parse_location, char const *doc_start ) {
298 return to_json_path_string(
299 find_json_path_stack_to( parse_location, doc_start ) );
300 }
301
302 [[nodiscard]] DAW_CPP20_CX_ALLOC std::string
303 find_json_path_to( json_exception const &jex, char const *doc_start ) {
304 return to_json_path_string(
305 find_json_path_stack_to( jex.parse_location( ), doc_start ) );
306 }
307
308 [[nodiscard]] constexpr std::size_t
309 find_line_number_of( char const *doc_pos, char const *doc_start ) {
310 daw_json_ensure( doc_pos != nullptr and doc_start != nullptr,
311 ErrorReason::UnexpectedEndOfData );
312 daw_json_ensure( json_details::less{ }( doc_start, doc_pos ),
313 ErrorReason::UnexpectedEndOfData );
314
315 return daw::algorithm::accumulate( doc_start,
316 doc_pos,
317 std::size_t{ },
318 []( std::size_t count, char c )
320 if( c == '\n' ) {
321 return count + 1;
322 }
323 return count;
324 } );
325 }
326
327 [[nodiscard]] constexpr std::size_t
328 find_line_number_of( json_path_node const &node, char const *doc_start ) {
329 return find_line_number_of( node.value_start( ), doc_start );
330 }
331
332 [[nodiscard]] constexpr std::size_t
333 find_column_number_of( char const *doc_pos, char const *doc_start ) {
334 daw_json_ensure( doc_pos != nullptr and doc_start != nullptr,
335 ErrorReason::UnexpectedEndOfData );
336 daw_json_ensure( json_details::less{ }( doc_start, doc_pos ),
337 ErrorReason::UnexpectedEndOfData );
338
339 auto const first = daw::reverse_iterator<char const *>( doc_pos );
340 auto const last = daw::reverse_iterator<char const *>( doc_start );
341 auto const pos = daw::algorithm::find( first, last, '\n' ) - first;
342 daw_json_ensure( pos >= 0, ErrorReason::Unknown );
343 return static_cast<std::size_t>( pos );
344 }
345
346 [[nodiscard]] constexpr std::size_t
347 find_column_number_of( json_path_node const &node, char const *doc_start ) {
348 return find_column_number_of( node.value_start( ), doc_start );
349 }
350 } // namespace DAW_JSON_VER
351} // namespace daw::json
constexpr json_path_node(JsonBaseParseTypes Type, std::string_view Name, long long Index, char const *ValueStart)
constexpr std::string_view name() const
The member name, only value for submembers of Class types.
constexpr char const * value_start() const
The beginning of the value's data in JSON document.
constexpr long long index() const
The element index, only valid for elements of Array types.
constexpr JsonBaseParseTypes type() const
What type of value is represented.
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
#define DAW_JSON_CPP23_STATIC_CALL_OP_DISABLE_WARNING
#define DAW_JSON_CPP23_STATIC_CALL_OP_CONST
#define DAW_JSON_CPP23_STATIC_CALL_OP_ENABLE_WARNING
#define DAW_JSON_CPP23_STATIC_CALL_OP
constexpr void json_event_parser(basic_json_value< P, A > bjv, Handler &&handler, options::parse_flags_t< ParseFlags... >)
constexpr std::size_t find_column_number_of(char const *doc_pos, char const *doc_start)
constexpr std::size_t find_line_number_of(char const *doc_pos, char const *doc_start)
DAW_ATTRIB_NOINLINE DAW_CPP20_CX_ALLOC std::string to_json_path_string(std::vector< json_path_node > const &path_stack)
Convert a json_path_node stack to a JSON Path string.
DAW_CPP20_CX_ALLOC std::string find_json_path_to(char const *parse_location, char const *doc_start)
DAW_CPP20_CX_ALLOC std::vector< json_path_node > find_json_path_stack_to(char const *parse_location, char const *doc_start)
Get the json_path_nodes representing the path to the nearest value's position in the document.
Customization point traits.
A name/value pair of string_view/json_value.
A non-owning container for arbitrary JSON values that allows movement/iteration through.
constexpr std::string_view get_string_view() const
Construct a string range of the current value. Strings start inside the quotes.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20