• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef UTIL_JSON_JSON_HELPERS_H_
6 #define UTIL_JSON_JSON_HELPERS_H_
7 
8 #include <chrono>
9 #include <cmath>
10 #include <functional>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 #include "absl/strings/string_view.h"
16 #include "json/value.h"
17 #include "platform/base/error.h"
18 #include "util/chrono_helpers.h"
19 #include "util/json/json_serialization.h"
20 #include "util/simple_fraction.h"
21 
22 // This file contains helper methods for parsing JSON, in an attempt to
23 // reduce boilerplate code when working with JsonCpp.
24 namespace openscreen {
25 namespace json {
26 
TryParseBool(const Json::Value & value,bool * out)27 inline bool TryParseBool(const Json::Value& value, bool* out) {
28   if (!value.isBool()) {
29     return false;
30   }
31   *out = value.asBool();
32   return true;
33 }
34 
35 // A general note about parsing primitives. "Validation" in this context
36 // generally means ensuring that the values are non-negative, excepting doubles
37 // which may be negative in some cases.
38 inline bool TryParseDouble(const Json::Value& value,
39                            double* out,
40                            bool allow_negative = false) {
41   if (!value.isDouble()) {
42     return false;
43   }
44   const double d = value.asDouble();
45   if (std::isnan(d)) {
46     return false;
47   }
48   if (!allow_negative && d < 0) {
49     return false;
50   }
51   *out = d;
52   return true;
53 }
54 
TryParseInt(const Json::Value & value,int * out)55 inline bool TryParseInt(const Json::Value& value, int* out) {
56   if (!value.isInt()) {
57     return false;
58   }
59   int i = value.asInt();
60   if (i < 0) {
61     return false;
62   }
63   *out = i;
64   return true;
65 }
66 
TryParseUint(const Json::Value & value,uint32_t * out)67 inline bool TryParseUint(const Json::Value& value, uint32_t* out) {
68   if (!value.isUInt()) {
69     return false;
70   }
71   *out = value.asUInt();
72   return true;
73 }
74 
TryParseString(const Json::Value & value,std::string * out)75 inline bool TryParseString(const Json::Value& value, std::string* out) {
76   if (!value.isString()) {
77     return false;
78   }
79   *out = value.asString();
80   return true;
81 }
82 
83 // We want to be more robust when we parse fractions then just
84 // allowing strings, this will parse numeral values such as
85 // value: 50 as well as value: "50" and value: "100/2".
TryParseSimpleFraction(const Json::Value & value,SimpleFraction * out)86 inline bool TryParseSimpleFraction(const Json::Value& value,
87                                    SimpleFraction* out) {
88   if (value.isInt()) {
89     int parsed = value.asInt();
90     if (parsed < 0) {
91       return false;
92     }
93     *out = SimpleFraction{parsed, 1};
94     return true;
95   }
96 
97   if (value.isString()) {
98     auto fraction_or_error = SimpleFraction::FromString(value.asString());
99     if (!fraction_or_error) {
100       return false;
101     }
102 
103     if (!fraction_or_error.value().is_positive() ||
104         !fraction_or_error.value().is_defined()) {
105       return false;
106     }
107     *out = std::move(fraction_or_error.value());
108     return true;
109   }
110   return false;
111 }
112 
TryParseMilliseconds(const Json::Value & value,milliseconds * out)113 inline bool TryParseMilliseconds(const Json::Value& value, milliseconds* out) {
114   int out_ms;
115   if (!TryParseInt(value, &out_ms) || out_ms < 0) {
116     return false;
117   }
118   *out = milliseconds(out_ms);
119   return true;
120 }
121 
122 template <typename T>
123 using Parser = std::function<bool(const Json::Value&, T*)>;
124 
125 // NOTE: array parsing methods reset the output vector to an empty vector in
126 // any error case. This is especially useful for optional arrays.
127 template <typename T>
TryParseArray(const Json::Value & value,Parser<T> parser,std::vector<T> * out)128 bool TryParseArray(const Json::Value& value,
129                    Parser<T> parser,
130                    std::vector<T>* out) {
131   out->clear();
132   if (!value.isArray() || value.empty()) {
133     return false;
134   }
135 
136   out->reserve(value.size());
137   for (Json::ArrayIndex i = 0; i < value.size(); ++i) {
138     T v;
139     if (!parser(value[i], &v)) {
140       out->clear();
141       return false;
142     }
143     out->push_back(v);
144   }
145 
146   return true;
147 }
148 
TryParseIntArray(const Json::Value & value,std::vector<int> * out)149 inline bool TryParseIntArray(const Json::Value& value, std::vector<int>* out) {
150   return TryParseArray<int>(value, TryParseInt, out);
151 }
152 
TryParseUintArray(const Json::Value & value,std::vector<uint32_t> * out)153 inline bool TryParseUintArray(const Json::Value& value,
154                               std::vector<uint32_t>* out) {
155   return TryParseArray<uint32_t>(value, TryParseUint, out);
156 }
157 
TryParseStringArray(const Json::Value & value,std::vector<std::string> * out)158 inline bool TryParseStringArray(const Json::Value& value,
159                                 std::vector<std::string>* out) {
160   return TryParseArray<std::string>(value, TryParseString, out);
161 }
162 
163 }  // namespace json
164 }  // namespace openscreen
165 
166 #endif  // UTIL_JSON_JSON_HELPERS_H_
167