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