JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
algorithm.hpp
Go to the documentation of this file.
1/** \file jsonv/algorithm.hpp
2 * A collection of algorithms a la `<algorithm>`.
3 *
4 * Copyright (c) 2014-2018 by Travis Gockel. All rights reserved.
5 *
6 * This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7 * as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8 * version.
9 *
10 * \author Travis Gockel (travis@gockelhut.com)
11**/
12#ifndef __JSONV_ALGORITHM_HPP_INCLUDED__
13#define __JSONV_ALGORITHM_HPP_INCLUDED__
14
15#include <jsonv/config.hpp>
16#include <jsonv/value.hpp>
17#include <jsonv/path.hpp>
18
19#include <cmath>
20#include <functional>
21#include <limits>
22
23namespace jsonv
24{
25
26class path;
27
28/** \addtogroup Algorithm
29 * \{
30 * A collection of useful free functions a la \c <algorithm>.
31**/
32
33/** Traits describing how to perform various aspects of comparison. This implementation for comparison is strict and is
34 * ultimately the one used by \c value::compare.
35 *
36 * \see compare
37**/
39{
40 /** Compare two kinds \a a and \a b. This should return 0 if the types are the same or if they are directly
41 * comparable (such as \c kind::integer and \c kind::decimal) -- if you return 0 for non-comparable types, you risk
42 * getting a \c kind_error thrown.
43 **/
44 static int compare_kinds(kind a, kind b)
45 {
46 int va = kindval(a);
47 int vb = kindval(b);
48 return va == vb ? 0 : va < vb ? -1 : 1;
49 }
50
51 /** Compare two boolean values. **/
52 static int compare_booleans(bool a, bool b)
53 {
54 return a == b ? 0
55 : a ? 1
56 : -1;
57 }
58
59 /** Compare two integer values. **/
60 static int compare_integers(std::int64_t a, std::int64_t b)
61 {
62 return a == b ? 0
63 : a < b ? -1
64 : 1;
65 }
66
67 /** Compare two decimal values. **/
68 static int compare_decimals(double a, double b)
69 {
70 return (std::abs(a - b) < (std::numeric_limits<double>::denorm_min() * 10.0)) ? 0
71 : (a < b) ? -1
72 : 1;
73 }
74
75 /** Compare two string values. **/
76 static int compare_strings(const std::string& a, const std::string& b)
77 {
78 return a.compare(b);
79 }
80
81 /** Compare two strings used for the keys of objects. **/
82 static int compare_object_keys(const std::string& a, const std::string& b)
83 {
84 return a.compare(b);
85 }
86
87 /** Compare two objects \e before comparing the values. The \c compare function will only check the contents of an
88 * object if this function returns 0.
89 **/
90 static int compare_objects_meta(const value&, const value&)
91 {
92 return 0;
93 }
94
95private:
96 static int kindval(kind k)
97 {
98 switch (k)
99 {
100 case jsonv::kind::null:
101 return 0;
102 case jsonv::kind::boolean:
103 return 1;
104 case jsonv::kind::integer:
105 case jsonv::kind::decimal:
106 return 2;
107 case jsonv::kind::string:
108 return 3;
109 case jsonv::kind::array:
110 return 4;
111 case jsonv::kind::object:
112 return 5;
113 default:
114 return -1;
115 }
116 }
117};
118
119/** Compare the values \a a and \a b using the comparison \a traits.
120 *
121 * \tparam TCompareTraits A type which should be compatible with the public signatures on the \c compare_traits class.
122**/
123template <typename TCompareTraits>
124int compare(const value& a, const value& b, const TCompareTraits& traits)
125{
126 if (&a == &b)
127 return 0;
128
129 if (int kindcmp = traits.compare_kinds(a.kind(), b.kind()))
130 return kindcmp;
131
132 switch (a.kind())
133 {
134 case jsonv::kind::null:
135 return 0;
136 case jsonv::kind::boolean:
137 return traits.compare_booleans(a.as_boolean(), b.as_boolean());
138 case jsonv::kind::integer:
139 // b might be a decimal type, but if they are both integers, compare directly
140 if (b.kind() == jsonv::kind::integer)
141 return traits.compare_integers(a.as_integer(), b.as_integer());
142 // fall through
143 case jsonv::kind::decimal:
144 return traits.compare_decimals(a.as_decimal(), b.as_decimal());
145 case jsonv::kind::string:
146 return traits.compare_strings(a.as_string(), b.as_string());
147 case jsonv::kind::array:
148 {
149 auto aiter = a.begin_array();
150 auto biter = b.begin_array();
151 for ( ; aiter != a.end_array() && biter != b.end_array(); ++aiter, ++biter)
152 if (int cmp = compare(*aiter, *biter, traits))
153 return cmp;
154 return aiter == a.end_array() ? biter == b.end_array() ? 0 : -1
155 : 1;
156 }
157 case jsonv::kind::object:
158 {
159 if (int objmetacmp = traits.compare_objects_meta(a, b))
160 return objmetacmp;
161
162 auto aiter = a.begin_object();
163 auto biter = b.begin_object();
164 for ( ; aiter != a.end_object() && biter != b.end_object(); ++aiter, ++biter)
165 {
166 if (int cmp = traits.compare_object_keys(aiter->first, biter->first))
167 return cmp;
168 if (int cmp = compare(aiter->second, biter->second, traits))
169 return cmp;
170 }
171 return aiter == a.end_object() ? biter == b.end_object() ? 0 : -1
172 : 1;
173 }
174 default:
175 return -1;
176 }
177}
178
179/// Compare the values \a a and \a b with strict comparison traits.
180///
181/// \see value::compare
182/// \see compare_icase
183JSONV_PUBLIC int compare(const value& a, const value& b);
184
185/// Compare the values \a a and \a b, but use case-insensitive matching on \c kind::string values. This does \e not use
186/// case-insensitive matching on the keys of objects!
187///
188/// \see compare
190
191/// The results of the \c diff operation.
193{
194 /// Elements that were the same between the two halves of the diff.
196
197 /// Elements that were unique to the left hand side of the diff.
199
200 /// Elements that were unique to the right hand side of the diff.
202};
203
204/** Find the differences and similarities between the structures of \a left and \a right. If \a left and \a right have
205 * a different \c kind (and the kind difference is not \c kind::integer and \c kind::decimal), \a left and \a right
206 * will be placed directly in the result. If they have the same \c kind and it is scalar, the values get a direct
207 * comparison. If they are the same, the result is moved to \c diff_result::same. If they are different, \a left and
208 * \a right are moved to \c diff_result::left and \c diff_result::right, respectively. For \c kind::array and
209 * \c kind::object, the \c value elements are compared recursively.
210**/
212
213/** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
214 * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
215 * \a input is \c kind::array, \c func is called for every value in the array and the output will be an array with each
216 * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
217 * transformed by \a func.
218 *
219 * \param func The function to apply to the element or elements of \a input.
220 * \param input The value to transform.
221**/
222JSONV_PUBLIC value map(const std::function<value (const value&)>& func,
223 const value& input
224 );
225
226/** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
227 * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
228 * \a input is \c kind::array, \c func is called for every value in the array and the output will be an array with each
229 * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
230 * transformed by \a func.
231 *
232 * \param func The function to apply to the element or elements of \a input.
233 * \param input The value to transform.
234 *
235 * \note
236 * This version of \c map provides only a basic exception-safety guarantee. If an exception is thrown while
237 * transforming a non-scalar \c kind, there is no rollback action, so \a input is left in a usable, but
238 * \e unpredictable state. If you need a strong exception guarantee, use the version of \c map that takes a constant
239 * reference to a \c value.
240**/
241JSONV_PUBLIC value map(const std::function<value (value)>& func,
242 value&& input
243 );
244
245/** Recursively walk the provided \a tree and call \a func for each item in the tree.
246 *
247 * \param tree The JSON value to traverse.
248 * \param func The function to call for each element in the tree.
249 * \param base_path The path to prepend to each output path to \a func. This can be useful if beginning traversal from
250 * inside of some JSON structure.
251 * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
252 * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
253 * for all entries in the tree.
254**/
256 const std::function<void (const path&, const value&)>& func,
257 const path& base_path,
258 bool leafs_only = false
259 );
260
261/** Recursively walk the provided \a tree and call \a func for each item in the tree.
262 *
263 * \param tree The JSON value to traverse.
264 * \param func The function to call for each element in the tree.
265 * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
266 * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
267 * for all entries in the tree.
268**/
270 const std::function<void (const path&, const value&)>& func,
271 bool leafs_only = false
272 );
273
274/** This class is used in \c merge_explicit for defining what the function should do in the cases of conflicts. **/
276{
277public:
278 virtual ~merge_rules() noexcept;
279
280 /** Called when merging a \c kind::object and the two objects share a key. The implementation can either throw or
281 * merge the keys.
282 *
283 * \param current_path is the merge \c path with the key that conflicted appended.
284 * \param a is the left-hand \c value to merge.
285 * \param b is the right-hand \c value to merge.
286 **/
287 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const = 0;
288
289 /** Called when \a a and \a b have \c kind values which are incompatible for merging. The implementation can either
290 * throw or coerce a merge.
291 *
292 * \param current_path \c path with the conflicting \c kind values.
293 * \param a is the left-hand \c value to merge.
294 * \param b is the right-hand \c value to merge.
295 **/
296 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const = 0;
297};
298
299/// An implementation of \c merge_rules that allows you to bind whatever functions you want to resolve conflicts.
301 public merge_rules
302{
303public:
304 using same_key_function = std::function<value (path&&, value&&, value&&)>;
305
306 using type_conflict_function = std::function<value (path&&, value&&, value&&)>;
307
308public:
309 dynamic_merge_rules(same_key_function same_key,
310 type_conflict_function type_conflict
311 );
312
313 virtual ~dynamic_merge_rules() noexcept;
314
315 same_key_function same_key;
316
317 type_conflict_function type_conflict;
318
319 /// \see merge_rules::resolve_same_key
320 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
321
322 /// \see merge_rules::resolve_type_conflict
323 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
324};
325
326/// These rules throw an exception on all conflicts.
328 public merge_rules
329{
330public:
331 /// \throws std::logic_error
332 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
333
334 /// \throws kind_error
335 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
336};
337
338/// These rules will recursively merge everything they can and coerce all values.
340 public merge_rules
341{
342public:
343 /// Recursively calls \c merge_explicit with the two values.
344 virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
345
346 /// Calls \c coerce_merge to combine the values.
347 virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
348};
349
350/// Merges two \c values, \a a and \a b into a single \c value.
351///
352/// The merging follows a few simple rules:
353///
354/// - If \a a.kind() != \a b.kind() and they are not \c kind::integer and \c kind::decimal, call \a on_type_conflict
355/// and return the result.
356/// - Otherwise, branch based on the (shared) type:
357/// - \c kind::object - Return a new object with all the values from \a a and \a b for the keys which are unique per
358/// object. For the keys which are shared, the value is the result of \a on_same_key.
359/// - \c kind::array - Return a new array with the values of \a b appended to \a a.
360/// - \c kind::string - Return a new string with \a b appended to \a a.
361/// - \c kind::boolean - Return `a.as_boolean() || b.as_boolean()`
362/// - \c kind::integer - If \a b is \c kind::integer, return `a + b` as an integer; otherwise, return it as a
363/// decimal.
364/// - \c kind::decimal - Return `a + b` as a decimal.
365///
366/// \param rules are the rules to merge with (see \c merge_rules).
367/// \param current_path The current \c path into the \c value that we are merging. This can be used to give more useful
368/// error information if we are merging recursively.
369/// \param a is a \c value to merge.
370/// \param b is a \c value to merge.
372 path current_path,
373 value a,
374 value b
375 );
376
378
380
381template <typename... TValue>
382value merge_explicit(const merge_rules& rules, path current_path, value a, value b, value c, TValue&&... rest)
383{
384 value ab = merge_explicit(rules, current_path, std::move(a), std::move(b));
385 return merge_explicit(rules,
386 std::move(current_path),
387 std::move(ab),
388 std::move(c),
389 std::forward<TValue>(rest)...
390 );
391}
392
393/** Merges all the provided \a values into a single \c value. If there are any key or type conflicts, an exception will
394 * be thrown.
395**/
396template <typename... TValue>
398{
400 path(),
401 std::forward<TValue>(values)...
402 );
403}
404
405/** Merges all the provided \a values into a single \c value. If there are any keys which are shared, their values are
406 * also merged.
407**/
408template <typename... TValue>
410{
412 path(),
413 std::forward<TValue>(values)...
414 );
415}
416
417/** Error thrown when an unrepresentable value is encountered in a JSON AST.
418 *
419 * \see validate
420**/
422 public std::runtime_error
423{
424public:
425 /** Special code for describing the error encountered. **/
426 enum class code
427 {
428 /** Encountered a number which is NaN or Infinity. **/
429 non_finite_number
430 };
431
432public:
434
435 virtual ~validation_error() noexcept;
436
437 /** Get the error code. **/
438 code error_code() const;
439
440 /** Get the path in the AST the error was found. **/
441 const jsonv::path& path() const;
442
443 /** Get the value that caused the error. **/
445
446private:
447 code _code;
448 jsonv::path _path;
449 jsonv::value _value;
450};
451
452JSONV_PUBLIC std::ostream& operator<<(std::ostream& os, const validation_error::code& code);
453
454/** Check that the provided \a val is perfectly representable as a JSON string. The JSON specification does not have
455 * support for things like non-finite floating-point numbers (\c NaN and \c infinity). This means \c value defined with
456 * these values will get serialized as \c null. This constitutes a loss of information, but not acting this way would
457 * lead to the encoder outputting invalid JSON text, which is completely unacceptable. Use this funciton to check that
458 * there will be no information loss when encoding.
459 *
460 * \throws validation_error if \a val contains an unrepresentable value.
461**/
462JSONV_PUBLIC void validate(const value& val);
463
464/** \} **/
465
466}
467
468#endif/*__JSONV_ALGORITHM_HPP_INCLUDED__*/
An implementation of merge_rules that allows you to bind whatever functions you want to resolve confl...
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
An adapter for enumeration types.
This class is used in merge_explicit for defining what the function should do in the cases of conflic...
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const =0
Called when merging a kind::object and the two objects share a key.
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const =0
Called when a and b have kind values which are incompatible for merging.
Represents an exact path in some JSON structure.
Definition path.hpp:83
These rules will recursively merge everything they can and coerce all values.
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
Calls coerce_merge to combine the values.
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
Recursively calls merge_explicit with the two values.
These rules throw an exception on all conflicts.
virtual value resolve_same_key(path &&current_path, value &&a, value &&b) const override
virtual value resolve_type_conflict(path &&current_path, value &&a, value &&b) const override
Error thrown when an unrepresentable value is encountered in a JSON AST.
code
Special code for describing the error encountered.
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition value.hpp:107
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_PUBLIC
This function or class is part of the public API for JSON Voorhees.
Definition config.hpp:102
value same
Elements that were the same between the two halves of the diff.
value right
Elements that were unique to the right hand side of the diff.
value left
Elements that were unique to the left hand side of the diff.
int compare(const value &a, const value &b, const TCompareTraits &traits)
Compare the values a and b using the comparison traits.
JSONV_PUBLIC diff_result diff(value left, value right)
Find the differences and similarities between the structures of left and right.
JSONV_PUBLIC value merge_explicit(const merge_rules &rules, path current_path, value a, value b)
Merges two values, a and b into a single value.
JSONV_PUBLIC value map(const std::function< value(const value &)> &func, const value &input)
Run a function over the values in the input.
JSONV_PUBLIC int compare_icase(const value &a, const value &b)
Compare the values a and b, but use case-insensitive matching on kind::string values.
value merge(TValue &&... values)
Merges all the provided values into a single value.
value merge_recursive(TValue &&... values)
Merges all the provided values into a single value.
JSONV_PUBLIC void traverse(const value &tree, const std::function< void(const path &, const value &)> &func, const path &base_path, bool leafs_only=false)
Recursively walk the provided tree and call func for each item in the tree.
The results of the diff operation.
kind
Describes the kind of data a value holds.
Definition kind.hpp:31
STL namespace.
Support for JSONPath.
Traits describing how to perform various aspects of comparison.
Definition algorithm.hpp:39
static int compare_strings(const std::string &a, const std::string &b)
Compare two string values.
Definition algorithm.hpp:76
static int compare_integers(std::int64_t a, std::int64_t b)
Compare two integer values.
Definition algorithm.hpp:60
static int compare_booleans(bool a, bool b)
Compare two boolean values.
Definition algorithm.hpp:52
static int compare_objects_meta(const value &, const value &)
Compare two objects before comparing the values.
Definition algorithm.hpp:90
static int compare_decimals(double a, double b)
Compare two decimal values.
Definition algorithm.hpp:68
static int compare_kinds(kind a, kind b)
Compare two kinds a and b.
Definition algorithm.hpp:44
static int compare_object_keys(const std::string &a, const std::string &b)
Compare two strings used for the keys of objects.
Definition algorithm.hpp:82
Copyright (c) 2012-2020 by Travis Gockel.