JSON Voorhees
Killer JSON for C++
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 
23 namespace jsonv
24 {
25 
26 class 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 
95 private:
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 **/
123 template <typename TCompareTraits>
124 int 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
183 **/
184 JSONV_PUBLIC int compare(const value& a, const value& b);
185 
186 /** Compare the values \a a and \a b, but use case-insensitive matching on \c kind::string values. This does \e not use
187  * case-insensitive matching on the keys of objects!
188  *
189  * \see compare
190 **/
191 JSONV_PUBLIC int compare_icase(const value& a, const value& b);
192 
193 /** The results of the \c diff operation. **/
195 {
196  /** Elements that were the same between the two halves of the diff. **/
198 
199  /** Elements that were unique to the left hand side of the diff. **/
201 
202  /** Elements that were unique to the right hand side of the diff. **/
204 };
205 
206 /** Find the differences and similarities between the structures of \a left and \a right. If \a left and \a right have
207  * a different \c kind (and the kind difference is not \c kind::integer and \c kind::decimal), \a left and \a right
208  * will be placed directly in the result. If they have the same \c kind and it is scalar, the values get a direct
209  * comparison. If they are the same, the result is moved to \c diff_result::same. If they are different, \a left and
210  * \a right are moved to \c diff_result::left and \c diff_result::right, respectively. For \c kind::array and
211  * \c kind::object, the \c value elements are compared recursively.
212 **/
214 
215 /** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
216  * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
217  * \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
218  * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
219  * transformed by \a func.
220  *
221  * \param func The function to apply to the element or elements of \a input.
222  * \param input The value to transform.
223 **/
224 JSONV_PUBLIC value map(const std::function<value (const value&)>& func,
225  const value& input
226  );
227 
228 /** Run a function over the values in the \a input. The behavior of this function is different, depending on the \c kind
229  * of \a input. For scalar kinds (\c kind::integer, \c kind::null, etc), \a func is called once with the value. If
230  * \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
231  * element transformed by \a func. If \a input is \c kind::object, the result will be an object with each key
232  * transformed by \a func.
233  *
234  * \param func The function to apply to the element or elements of \a input.
235  * \param input The value to transform.
236  *
237  * \note
238  * This version of \c map provides only a basic exception-safety guarantee. If an exception is thrown while
239  * transforming a non-scalar \c kind, there is no rollback action, so \a input is left in a usable, but
240  * \e unpredictable state. If you need a strong exception guarantee, use the version of \c map that takes a constant
241  * reference to a \c value.
242 **/
243 JSONV_PUBLIC value map(const std::function<value (value)>& func,
244  value&& input
245  );
246 
247 /** Recursively walk the provided \a tree and call \a func for each item in the tree.
248  *
249  * \param tree The JSON value to traverse.
250  * \param func The function to call for each element in the tree.
251  * \param base_path The path to prepend to each output path to \a func. This can be useful if beginning traversal from
252  * inside of some JSON structure.
253  * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
254  * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
255  * for all entries in the tree.
256 **/
257 JSONV_PUBLIC void traverse(const value& tree,
258  const std::function<void (const path&, const value&)>& func,
259  const path& base_path,
260  bool leafs_only = false
261  );
262 
263 /** Recursively walk the provided \a tree and call \a func for each item in the tree.
264  *
265  * \param tree The JSON value to traverse.
266  * \param func The function to call for each element in the tree.
267  * \param leafs_only If true, call \a func only when the current path is a "leaf" value (\c string, \c integer,
268  * \c decimal, \c boolean, or \c null \e or an empty \c array or \c object); if false, call \a func
269  * for all entries in the tree.
270 **/
271 JSONV_PUBLIC void traverse(const value& tree,
272  const std::function<void (const path&, const value&)>& func,
273  bool leafs_only = false
274  );
275 
276 /** This class is used in \c merge_explicit for defining what the function should do in the cases of conflicts. **/
278 {
279 public:
280  virtual ~merge_rules() noexcept;
281 
282  /** Called when merging a \c kind::object and the two objects share a key. The implementation can either throw or
283  * merge the keys.
284  *
285  * \param current_path is the merge \c path with the key that conflicted appended.
286  * \param a is the left-hand \c value to merge.
287  * \param b is the right-hand \c value to merge.
288  **/
289  virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const = 0;
290 
291  /** Called when \a a and \a b have \c kind values which are incompatible for merging. The implementation can either
292  * throw or coerce a merge.
293  *
294  * \param current_path \c path with the conflicting \c kind values.
295  * \param a is the left-hand \c value to merge.
296  * \param b is the right-hand \c value to merge.
297  **/
298  virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const = 0;
299 };
300 
301 /** An implementation of \c merge_rules that allows you to bind whatever functions you want to resolve conflicts. **/
303  public merge_rules
304 {
305 public:
306  using same_key_function = std::function<value (path&&, value&&, value&&)>;
307 
308  using type_conflict_function = std::function<value (path&&, value&&, value&&)>;
309 
310 public:
311  dynamic_merge_rules(same_key_function same_key,
312  type_conflict_function type_conflict
313  );
314 
315  virtual ~dynamic_merge_rules() noexcept;
316 
317  same_key_function same_key;
318 
319  type_conflict_function type_conflict;
320 
321  /// \see merge_rules::resolve_same_key
322  virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
323 
324  /// \see merge_rules::resolve_type_conflict
325  virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
326 };
327 
328 /** These rules throw an exception on all conflicts. **/
330  public merge_rules
331 {
332 public:
333  /** \throws std::logic_error **/
334  virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
335 
336  /** \throws kind_error **/
337  virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
338 };
339 
340 /** These rules will recursively merge everything they can and coerce all values. **/
342  public merge_rules
343 {
344 public:
345  /** Recursively calls \c merge_explicit with the two values. **/
346  virtual value resolve_same_key(path&& current_path, value&& a, value&& b) const override;
347 
348  /** Calls \c coerce_merge to combine the values. **/
349  virtual value resolve_type_conflict(path&& current_path, value&& a, value&& b) const override;
350 };
351 
352 /** Merges two \c values, \a a and \a b into a single \c value.
353  *
354  * The merging follows a few simple rules:
355  *
356  * - If \a a.kind() != \a b.kind() and they are not \c kind::integer and \c kind::decimal, call \a on_type_conflict
357  * and return the result.
358  * - Otherwise, branch based on the (shared) type:
359  * - \c kind::object - Return a new object with all the values from \a a and \a b for the keys which are unique per
360  * object. For the keys which are shared, the value is the result of \a on_same_key.
361  * - \c kind::array - Return a new array with the values of \a b appended to \a a.
362  * - \c kind::string - Return a new string with \a b appended to \a a.
363  * - \c kind::boolean - Return `a.as_boolean() || b.as_boolean()`
364  * - \c kind::integer - If \b is \c kind::integer, return `a + b` as an integer; otherwise, return it as a decimal.
365  * - \c kind::decimal - Return `a + b` as a decimal.
366  *
367  * \param rules are the rules to merge with (see \c merge_rules).
368  * \param current_path The current \c path into the \c value that we are merging. This can be used to give more useful
369  * error information if we are merging recursively.
370  * \param a is a \c value to merge.
371  * \param b is a \c value to merge.
372 **/
374  path current_path,
375  value a,
376  value b
377  );
378 
379 JSONV_PUBLIC value merge_explicit(const merge_rules&, const path&, value a);
380 
381 JSONV_PUBLIC value merge_explicit(const merge_rules&, const path&);
382 
383 template <typename... TValue>
384 value merge_explicit(const merge_rules& rules, path current_path, value a, value b, value c, TValue&&... rest)
385 {
386  value ab = merge_explicit(rules, current_path, std::move(a), std::move(b));
387  return merge_explicit(rules,
388  std::move(current_path),
389  std::move(ab),
390  std::move(c),
391  std::forward<TValue>(rest)...
392  );
393 }
394 
395 /** Merges all the provided \a values into a single \c value. If there are any key or type conflicts, an exception will
396  * be thrown.
397 **/
398 template <typename... TValue>
399 value merge(TValue&&... values)
400 {
401  return merge_explicit(throwing_merge_rules(),
402  path(),
403  std::forward<TValue>(values)...
404  );
405 }
406 
407 /** Merges all the provided \a values into a single \c value. If there are any keys which are shared, their values are
408  * also merged.
409 **/
410 template <typename... TValue>
411 value merge_recursive(TValue&&... values)
412 {
413  return merge_explicit(recursive_merge_rules(),
414  path(),
415  std::forward<TValue>(values)...
416  );
417 }
418 
419 /** Error thrown when an unrepresentable value is encountered in a JSON AST.
420  *
421  * \see validate
422 **/
424  public std::runtime_error
425 {
426 public:
427  /** Special code for describing the error encountered. **/
428  enum class code
429  {
430  /** Encountered a number which is NaN or Infinity. **/
431  non_finite_number
432  };
433 
434 public:
435  explicit validation_error(code code_, jsonv::path path_, jsonv::value value_);
436 
437  virtual ~validation_error() noexcept;
438 
439  /** Get the error code. **/
440  code error_code() const;
441 
442  /** Get the path in the AST the error was found. **/
443  const jsonv::path& path() const;
444 
445  /** Get the value that caused the error. **/
446  const jsonv::value& value() const;
447 
448 private:
449  code _code;
450  jsonv::path _path;
451  jsonv::value _value;
452 };
453 
454 JSONV_PUBLIC std::ostream& operator<<(std::ostream& os, const validation_error::code& code);
455 
456 /** Check that the provided \a val is perfectly representable as a JSON string. The JSON specification does not have
457  * support for things like non-finite floating-point numbers (\c NaN and \c infinity). This means \c value defined with
458  * these values will get serialized as \c null. This constitutes a loss of information, but not acting this way would
459  * lead to the encoder outputting invalid JSON text, which is completely unacceptable. Use this funciton to check that
460  * there will be no information loss when encoding.
461  *
462  * \throws validation_error if \a val contains an unrepresentable value.
463 **/
464 JSONV_PUBLIC void validate(const value& val);
465 
466 /** \} **/
467 
468 }
469 
470 #endif/*__JSONV_ALGORITHM_HPP_INCLUDED__*/
value same
Elements that were the same between the two halves of the diff.
Definition: algorithm.hpp:197
JSONV_PUBLIC int compare(const value &a, const value &b)
Compare the values a and b with strict comparison traits.
value merge_recursive(TValue &&...values)
Merges all the provided values into a single value.
Definition: algorithm.hpp:411
static int compare_kinds(kind a, kind b)
Compare two kinds a and b.
Definition: algorithm.hpp:44
object_iterator end_object()
Get an iterator to the one past the end of this object.
bool as_boolean() const
Get this value as a boolean.
These rules throw an exception on all conflicts.
Definition: algorithm.hpp:329
int64_t as_integer() const
Get this value as an integer.
static int compare_objects_meta(const value &, const value &)
Compare two objects before comparing the values.
Definition: algorithm.hpp:90
Traits describing how to perform various aspects of comparison.
Definition: algorithm.hpp:38
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
value merge(TValue &&...values)
Merges all the provided values into a single value.
Definition: algorithm.hpp:399
These rules will recursively merge everything they can and coerce all values.
Definition: algorithm.hpp:341
array_iterator end_array()
Get an iterator to the end of this array.
Copyright (c) 2014-2019 by Travis Gockel.
const std::string & as_string() const
Get this value as a string.
JSONV_PUBLIC diff_result diff(value left, value right)
Find the differences and similarities between the structures of left and right.
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.
static int compare_decimals(double a, double b)
Compare two decimal values.
Definition: algorithm.hpp:68
Support for JSONPath.
Represents an exact path in some JSON structure.
Definition: path.hpp:82
value left
Elements that were unique to the left hand side of the diff.
Definition: algorithm.hpp:200
JSONV_PUBLIC void validate(const value &val)
Check that the provided val is perfectly representable as a JSON string.
JSONV_PUBLIC void traverse(const value &tree, const std::function< void(const path &, const value &)> &func, bool leafs_only=false)
Recursively walk the provided tree and call func for each item in the tree.
object_iterator begin_object()
Get an iterator to the first key-value pair in this object.
The results of the diff operation.
Definition: algorithm.hpp:194
static int compare_strings(const std::string &a, const std::string &b)
Compare two string values.
Definition: algorithm.hpp:76
jsonv::kind kind() const
Get this value&#39;s kind.
Definition: value.hpp:562
kind
Describes the kind of data a value holds.
Definition: value.hpp:69
array_iterator begin_array()
Get an iterator to the beginning of this array.
Error thrown when an unrepresentable value is encountered in a JSON AST.
Definition: algorithm.hpp:423
value right
Elements that were unique to the right hand side of the diff.
Definition: algorithm.hpp:203
This class is used in merge_explicit for defining what the function should do in the cases of conflic...
Definition: algorithm.hpp:277
An implementation of merge_rules that allows you to bind whatever functions you want to resolve confl...
Definition: algorithm.hpp:302
JSONV_PUBLIC value map(const std::function< value(value)> &func, value &&input)
Run a function over the values in the input.
code
Special code for describing the error encountered.
Definition: algorithm.hpp:428
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.
double as_decimal() const
Get this value as a decimal.
static int compare_integers(std::int64_t a, std::int64_t b)
Compare two integer values.
Definition: algorithm.hpp:60
#define JSONV_PUBLIC
This function or class is part of the public API for JsonVoorhees.
Definition: config.hpp:104
Represents a single JSON value, which can be any one of a potential kind, each behaving slightly diff...
Definition: value.hpp:131
static int compare_booleans(bool a, bool b)
Compare two boolean values.
Definition: algorithm.hpp:52
Copyright (c) 2012-2018 by Travis Gockel.