// Copyright 2019 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef UTIL_JSON_JSON_HELPERS_H_ #define UTIL_JSON_JSON_HELPERS_H_ #include #include #include #include #include #include #include "json/value.h" #include "platform/base/error.h" #include "util/chrono_helpers.h" #include "util/json/json_serialization.h" #include "util/simple_fraction.h" // This file contains helper methods for parsing JSON, in an attempt to // reduce boilerplate code when working with JsonCpp. namespace openscreen::json { inline bool TryParseBool(const Json::Value& value, bool* out) { if (!value.isBool()) { return false; } *out = value.asBool(); return true; } // A general note about parsing primitives. "Validation" in this context // generally means ensuring that the values are non-negative, excepting doubles // which may be negative in some cases. inline bool TryParseDouble(const Json::Value& value, double* out, bool allow_negative = false) { if (!value.isDouble()) { return false; } const double d = value.asDouble(); if (std::isnan(d)) { return false; } if (!allow_negative && d < 0) { return false; } *out = d; return true; } inline bool TryParseInt(const Json::Value& value, int* out) { if (!value.isInt()) { return false; } int i = value.asInt(); if (i < 0) { return false; } *out = i; return true; } inline bool TryParseUint(const Json::Value& value, uint32_t* out) { if (!value.isUInt()) { return false; } *out = value.asUInt(); return true; } inline bool TryParseString(const Json::Value& value, std::string* out) { if (!value.isString()) { return false; } *out = value.asString(); return true; } // We want to be more robust when we parse fractions then just // allowing strings, this will parse numeral values such as // value: 50 as well as value: "50" and value: "100/2". inline bool TryParseSimpleFraction(const Json::Value& value, SimpleFraction* out) { if (value.isInt()) { int parsed = value.asInt(); if (parsed < 0) { return false; } *out = SimpleFraction{parsed, 1}; return true; } if (value.isString()) { auto fraction_or_error = SimpleFraction::FromString(value.asString()); if (!fraction_or_error) { return false; } if (!fraction_or_error.value().is_positive() || !fraction_or_error.value().is_defined()) { return false; } *out = std::move(fraction_or_error.value()); return true; } return false; } inline bool TryParseMilliseconds(const Json::Value& value, milliseconds* out) { int out_ms; if (!TryParseInt(value, &out_ms) || out_ms < 0) { return false; } *out = milliseconds(out_ms); return true; } template using Parser = std::function; // NOTE: array parsing methods reset the output vector to an empty vector in // any error case. This is especially useful for optional arrays. template bool TryParseArray(const Json::Value& value, Parser parser, std::vector* out) { out->clear(); if (!value.isArray() || value.empty()) { return false; } out->reserve(value.size()); for (Json::ArrayIndex i = 0; i < value.size(); ++i) { T v; if (!parser(value[i], &v)) { out->clear(); return false; } out->push_back(v); } return true; } inline bool TryParseIntArray(const Json::Value& value, std::vector* out) { return TryParseArray(value, TryParseInt, out); } inline bool TryParseUintArray(const Json::Value& value, std::vector* out) { return TryParseArray(value, TryParseUint, out); } inline bool TryParseStringArray(const Json::Value& value, std::vector* out) { return TryParseArray(value, TryParseString, out); } inline bool TryParseNestedStringArray( const Json::Value& value, std::vector>* out) { return TryParseArray>(value, TryParseStringArray, out); } template Json::Value PrimitiveVectorToJson(const std::vector& vec) { Json::Value array(Json::ValueType::arrayValue); array.resize(vec.size()); for (Json::Value::ArrayIndex i = 0; i < vec.size(); ++i) { array[i] = Json::Value(vec[i]); } return array; } inline Json::Value NestedStringArrayToJson( const std::vector>& vec) { Json::Value array(Json::ValueType::arrayValue); array.resize(vec.size()); for (Json::Value::ArrayIndex i = 0; i < vec.size(); ++i) { array[i] = PrimitiveVectorToJson(vec[i]); } return array; } inline bool Contains(const Json::Value& array, std::string_view value) { if (!array.isArray()) { return false; } for (const Json::Value& entry : array) { if (entry.isString() && entry.asString() == value) { return true; } } return false; } } // namespace openscreen::json #endif // UTIL_JSON_JSON_HELPERS_H_