HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_JSONPath.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_JSONPath.h (UT Library, C++)
7  *
8  * COMMENTS:
9  */
10 
11 #ifndef __UT_JSONPath__
12 #define __UT_JSONPath__
13 
14 #include "UT_API.h"
15 #include "UT_Set.h"
16 #include "UT_StringHolder.h"
17 #include "UT_JSONValueArray.h"
18 #include "UT_JSONValueMap.h"
19 #include "UT_WorkBuffer.h"
20 #include <SYS/SYS_ParseNumber.h>
21 
22 /// @{
23 /// Utility functions to help with parsing
24 UT_API extern const char *UTjsonExtractPath(UT_StringView &path, const char *x);
26 /// @}
27 
28 /// @brief Implement simple JSONPath parser.
29 ///
30 /// This supports a very simple JSONPath syntax:
31 /// - `$`: root object
32 /// - `@, this`: current object
33 /// - `path`: named operator
34 /// - `. or []`: child operator (path or index)
35 /// - `[,]`: union operator (union of paths or indices)
36 /// - `..`: recursive descent
37 /// - `*`: wildcard
38 ///
39 /// Limitations:
40 /// - Array slicing is not supported
41 /// - No expression support - you can't do `$.book[?(@.cost<7)].title`)
42 /// Extensions:
43 /// - `path` objects can be regex expressions
44 ///
45 /// As there's no formal syntax for JSONPath, this class was inspired from
46 /// https://goessner.net/articles/JsonPath/
47 ///
48 /// For example, with the JSON: @code
49 /// {
50 /// "animals" : [
51 /// {
52 /// "type" : "cow",
53 /// "legs" : 4,
54 /// "wings" : false
55 /// },
56 /// {
57 /// "type" : "cat",
58 /// "legs" : 4,
59 /// "wings" : false
60 /// },
61 /// {
62 /// "type" : "budgie",
63 /// "legs" : 2,
64 /// "wings" : true
65 /// }
66 /// ],
67 /// "vehicles" : [ ]
68 /// }
69 /// @endcode
70 ///
71 /// Paths would resolve:
72 /// @code
73 /// $.animals[1] -> { "type" : "cat", "legs" : 4, "wings : false }
74 /// $.animals[0,1]."type" -> [ "cow", "cat" ]
75 /// $[animals].."type" -> [ "cow", "cat", "budgie" ]
76 /// $[vehicles] -> [ ]
77 /// @endcode
78 ///
79 /// The template expects a functor class to provide information about the
80 /// object, which needs to have:
81 /// - `static inline bool isArray(const T *obj);`
82 /// - `static inline bool isMap(const T *obj);`
83 /// - `static inline const T *arrayItem(const T *obj, exint idx);`
84 /// - `static inline const T *mapItem(const T *obj, const char *name);`
85 /// - `static inline iterator arrayBegin(const T &obj);`
86 /// - `static inline iterator arrayEnd(const T &obj);`
87 /// - `static inline iterator mapBegin(const T &obj);`
88 /// - `static inline iterator mapEnd(const T &obj);`
89 template <typename T, class FUNC>
91 {
92 public:
93  static inline bool find(UT_Set<const T *> &matches, const T &src,
94  const char *expr)
95  {
96  matches.clear();
97  if (!UTisstring(expr))
98  return false;
99  traverse(matches, &src, &src, expr);
100 
101  return matches.size() > 0;
102  }
103 
104 private:
105  static inline void traverseArray(const T &item,
106  UT_Set<const T *> &matches,
107  const T *root,
108  const char *expr)
109  {
110  for (auto it = FUNC::arrayBegin(item), n = FUNC::arrayEnd(item);
111  it != n; ++it)
112  traverse(matches, root, *it, expr);
113  }
114  static inline void traverseMap(const T &item,
115  UT_Set<const T *> &matches,
116  const T *root,
117  const char *expr)
118  {
119  for (auto it = FUNC::mapBegin(item), n = FUNC::mapEnd(item);
120  it != n; ++it)
121  traverse(matches, root, it->second, expr);
122  }
123 
124  static inline void traverse(UT_Set<const T *> &matches,
125  const T *root,
126  const T *src,
127  const char *expr)
128  {
129  if (*expr == '$')
130  {
131  src = root;
132  expr++;
133  }
134  if (!*expr)
135  {
136  matches.insert(src);
137  return;
138  }
139  if (!FUNC::isArray(src) && !FUNC::isMap(src))
140  {
141  // Made it to a leaf without matching the expression
142  return;
143  }
145  switch (*expr)
146  {
147  case '.':
148  if (!expr[1])
149  return; // Invalid . terminator
150  if (expr[1] == '.') // Recursive match
151  {
152  if (expr[2] == '.')
153  return; // Invalid ... expression
154  if (FUNC::isArray(src))
155  traverseArray(*src, matches, root, expr);
156  else
157  traverseMap(*src, matches, root, expr);
158  // Now, advnce to the next dot and check if our children
159  // match
160  traverse(matches, root, src, expr+2);
161  return;
162  }
163  expr = UTjsonExtractPath(path, expr+1);
164  if (!path)
165  return; // Invalid path;
166  break;
167  case '[':
168  if (!expr[1])
169  return; // Invalid [] terminator
170  expr = UTjsonExtractPath(path, expr+1);
171  if (!path)
172  return;
173  if (*expr == ']')
174  ++expr;
175  else if (*expr == '.')
176  return; // Invalid [.] expression
177  break;
178  default: // Explicit path
179  expr = UTjsonExtractPath(path, expr);
180  break;
181  }
182  if (!path)
183  return;
184 
185  UT_StringView part;
186  while (UTjsonSplitComma(part, path))
187  {
188  if (part == "*")
189  {
190  if (FUNC::isArray(src))
191  traverseArray(*src, matches, root, expr);
192  else
193  traverseMap(*src, matches, root, expr);
194  }
195  else if (FUNC::isArray(src))
196  {
197  exint idx;
198  const char *end = part.end();
199  if (SYSparseInteger(part.begin(), end, idx)
201  {
202  const T *kid = FUNC::arrayItem(src, idx);
203  if (kid)
204  traverse(matches, root, kid, expr);
205  }
206  }
207  else
208  {
209  UT_WorkBuffer str;
210  str.append(part.begin(), part.length());
211  const T *kid = FUNC::mapItem(src, str.buffer());
212  if (kid)
213  traverse(matches, root, kid, expr);
214  }
215  }
216  }
217 };
218 
219 /// @brief Class to implement UT_JSONPath_T parsing for a UT_JSONValue.
221 {
222 public:
225  static inline bool isArray(const UT_JSONValue *obj)
226  {
227  return obj->getType() == UT_JSONValue::JSON_ARRAY;
228  }
229  static inline bool isMap(const UT_JSONValue *obj)
230  {
231  return obj->getType() == UT_JSONValue::JSON_MAP;
232  }
233  static inline const UT_JSONValue *
234  arrayItem(const UT_JSONValue *obj, exint idx)
235  {
236  const UT_JSONValueArray *arr = obj->getArray();
237  return arr && idx >= 0 && idx < arr->size() ? arr->get(idx) : nullptr;
238  }
239 
240  static inline const UT_JSONValue *
241  mapItem(const UT_JSONValue *obj, const char *name)
242  {
243  const UT_JSONValueMap *map = obj->getMap();
244  return map ? map->get(name) : nullptr;
245  }
246  static inline array_iterator arrayBegin(const UT_JSONValue &obj)
247  { return obj.getArray()->begin(); }
248  static inline array_iterator arrayEnd(const UT_JSONValue &obj)
249  { return obj.getArray()->end(); }
250  static inline map_iterator mapBegin(const UT_JSONValue &obj)
251  { return obj.getMap()->begin(); }
252  static inline map_iterator mapEnd(const UT_JSONValue &obj)
253  { return obj.getMap()->end(); }
254 };
255 
256 /// @class UT_JSONPath
257 /// Specialization of UT_JSONPath_T for UT_JSONValue.
259 
260 #endif
Definition: UT_Set.h:58
UT_JSONValueMap stores a map/dictionary of UT_JSONValue objects.
SYS_NO_DISCARD_RESULT UT_JSONValueArray * getArray() const
Get the array value (may return a NULL pointer)
void clear()
Definition: UT_Set.h:124
UT_API const char * UTjsonExtractPath(UT_StringView &path, const char *x)
SYS_NO_DISCARD_RESULT const UT_JSONValue * get(int64 i) const
Access a const entry by index.
static bool isArray(const UT_JSONValue *obj)
Definition: UT_JSONPath.h:225
UT_JSONValueArray stores a list of UT_JSONValue objects.
GLsizei const GLchar *const * path
Definition: glcorearb.h:3341
int64 exint
Definition: SYS_Types.h:125
SYS_FORCE_INLINE const char * buffer() const
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE const_iterator end() const
Returns a constant iterator pointing to the end of the string.
#define UT_API
Definition: UT_API.h:14
static bool isMap(const UT_JSONValue *obj)
Definition: UT_JSONPath.h:229
const_iterator end() const
Class to implement UT_JSONPath_T parsing for a UT_JSONValue.
Definition: UT_JSONPath.h:220
A utility class to do read-only operations on a subset of an existing string.
Definition: UT_StringView.h:39
GLdouble n
Definition: glcorearb.h:2008
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE exint length() const
Returns the length of the string in bytes.
const_iterator begin() const
UT_JSONValueArray::const_iterator array_iterator
Definition: UT_JSONPath.h:223
SYS_API SYS_ParseStatus SYSparseInteger(const char *begin, const char *&end, int8 &number, int base=0, SYS_ParseFlags flags=SYS_ParseFlags::None)
SYS_NO_DISCARD_RESULT UT_JSONValueMap * getMap() const
Get the map value (may return a NULL pointer)
GLuint GLuint end
Definition: glcorearb.h:475
GLuint const GLchar * name
Definition: glcorearb.h:786
GLint GLenum GLint x
Definition: glcorearb.h:409
Implement simple JSONPath parser.
Definition: UT_JSONPath.h:90
static map_iterator mapEnd(const UT_JSONValue &obj)
Definition: UT_JSONPath.h:252
static const UT_JSONValue * mapItem(const UT_JSONValue *obj, const char *name)
Definition: UT_JSONPath.h:241
UT_API bool UTjsonSplitComma(UT_StringView &part, UT_StringView &path)
static bool find(UT_Set< const T * > &matches, const T &src, const char *expr)
Definition: UT_JSONPath.h:93
SYS_FORCE_INLINE bool UTisstring(const char *s)
SYS_FORCE_INLINE void append(char character)
A number was successfully parsed.
SYS_NO_DISCARD_RESULT Type getType() const
Get the type of data stored in the object.
Definition: UT_JSONValue.h:165
Class to store JSON objects as C++ objects.
Definition: UT_JSONValue.h:99
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE const_iterator begin() const
Returns a constant iterator pointing to the beginning of the string.
static const UT_JSONValue * arrayItem(const UT_JSONValue *obj, exint idx)
Definition: UT_JSONPath.h:234
UT_Array< UT_JSONValue * >::const_iterator const_iterator
static array_iterator arrayBegin(const UT_JSONValue &obj)
Definition: UT_JSONPath.h:246
static array_iterator arrayEnd(const UT_JSONValue &obj)
Definition: UT_JSONPath.h:248
static map_iterator mapBegin(const UT_JSONValue &obj)
Definition: UT_JSONPath.h:250
SYS_FORCE_INLINE const UT_JSONValue * get(int64 i) const
Access a const entry by index.
const iterator const_iterator
SYS_FORCE_INLINE int64 size() const
Return size of the array.
GLenum src
Definition: glcorearb.h:1793