@@ -1908,13 +1908,19 @@ class string_id_reader : public generic_typed_reader<string_id_reader<T>>
1908
1908
1909
1909
/* *
1910
1910
* Loads std::pair of [K = string_id, V = int/float] values from JSON -- usually into an std::map
1911
+ * It can load either an array, an object, or a single value
1911
1912
* Accepted formats for elements in an array:
1912
1913
* 1. A named key/value pair object: "addiction_type": [ { "addiction": "caffeine", "potential": 3 } ]
1913
1914
* 2. A key/value pair array: "addiction_type": [ [ "caffeine", 3 ] ]
1914
1915
* 3. A single value: "addiction_type": [ "caffeine" ]
1915
- * A single value can also be provided outside of an array, e.g. "addiction_type": "caffeine"
1916
+ * For an object, only the following format is supported
1917
+ * 4. "addiction_type": { "caffeine": 3, "alcohol": 2 }
1918
+ * A single value can be provided as follows:
1919
+ * 5. "addiction_type": "caffeine"
1916
1920
* For single values, weights are assigned default_weight
1917
1921
*/
1922
+ // TODO: format 1 is quite gross, and just accepts arbitrary keys. The only way it works is by limiting
1923
+ // the types of V, which is unfortunate
1918
1924
template <typename K, typename V>
1919
1925
class weighted_string_id_reader : public generic_typed_reader <weighted_string_id_reader<K, V>>
1920
1926
{
@@ -1966,50 +1972,86 @@ class weighted_string_id_reader : public generic_typed_reader<weighted_string_id
1966
1972
}
1967
1973
};
1968
1974
1975
+ // For loading pairs from a JSON object. { "a1": b1, "a2": b2 }, etc
1976
+ // Only supports JSON objects! Json objects are not ordered, so if you're using something where
1977
+ // the order matters, use pair_reader.
1969
1978
template <typename K, typename V>
1970
1979
class generic_map_reader : public generic_typed_reader <generic_map_reader<K, V>>
1971
1980
{
1972
1981
public:
1973
1982
static constexpr bool read_objects = true ;
1974
1983
1984
+ static_assert ( key_from_json_string<K>::valid, " Type K must have a known conversion from string" );
1985
+
1975
1986
std::pair<K, V> get_next ( const JsonValue &jv ) const {
1976
1987
const JsonMember *jm = dynamic_cast <const JsonMember *>( &jv );
1977
1988
if ( jm == nullptr ) {
1978
1989
jv.throw_error ( " not part of a JsonObject" );
1979
1990
}
1980
- K key ( jm->name () );
1991
+ K key = key_from_json_string<K>() ( jm->name () );
1981
1992
V value;
1982
1993
jv.read ( value, true );
1983
1994
return std::pair<K, V>( key, value );
1984
1995
}
1985
1996
};
1986
1997
1987
- // Support shorthand for a single value.
1988
- template <typename T>
1989
- class pair_reader : public generic_typed_reader <pair_reader<T>>
1998
+ // Supports three formats:
1999
+ // 1. [ a, b ]
2000
+ // 2. { "a": b }, if there exists a key_from_json_string specialization for a
2001
+ // 3. a, if T = U and a is not represented in JSON as an array or object (iff the object format is valid)
2002
+ // This would load [ "a", [ "b", "c" ], { "d": "e" } ] into three pairs:
2003
+ // ("a", "a"), ("b", "c"), ("d", "e")
2004
+ //
2005
+ // If you only need format 1, you can also use json_read_reader<std::pair<T, U>>
2006
+ //
2007
+ // TODO: constructor argument to enable/disable the shorthand format?
2008
+ template <typename T, typename U = T>
2009
+ class pair_reader : public generic_typed_reader <pair_reader<T, U>>
1990
2010
{
1991
2011
public:
1992
- std::pair<T, T> get_next ( const JsonValue &jv ) const {
1993
- if ( jv.test_float () ) {
1994
- T val;
1995
- jv.read ( val, true );
1996
- return std::make_pair ( val, val );
1997
- }
1998
- if ( !jv.test_array () ) {
1999
- jv.throw_error ( " bad pair" );
2012
+ std::pair<T, U> get_next ( const JsonValue &jv ) const {
2013
+ // elements of the pair to return
2014
+ T a;
2015
+ U b;
2016
+ // [ a, b ]
2017
+ if ( jv.test_array () ) {
2018
+ JsonArray ja = jv.get_array ();
2019
+ if ( ja.size () != 2 ) {
2020
+ ja.throw_error ( " Must have 2 elements" );
2021
+ }
2022
+ ja[0 ].read ( a, true );
2023
+ ja[1 ].read ( b, true );
2024
+ return std::make_pair ( a, b );
2025
+ }
2026
+ // { "a": b }
2027
+ if constexpr ( key_from_json_string<T>::valid ) {
2028
+ if ( jv.test_object () ) {
2029
+ JsonObject jo = jv.get_object ();
2030
+ if ( jo.size () != 1 ) {
2031
+ jo.throw_error ( " bad object pair, has too many elements" );
2032
+ }
2033
+ // gross - we need to get the name of the member, this just unwraps it
2034
+ for ( JsonMember jm : jo ) {
2035
+ a = key_from_json_string<T>()( jm.name () );
2036
+ jm.read ( b, true );
2037
+ return std::make_pair ( a, b );
2038
+ }
2039
+ }
2000
2040
}
2001
- JsonArray ja = jv.get_array ();
2002
- if ( ja.size () != 2 ) {
2003
- ja.throw_error ( " Must have 2 elements" );
2041
+ // shorthand for a = b
2042
+ if constexpr ( std::is_same_v<T, U> ) {
2043
+ jv.read ( a, true );
2044
+ b = a;
2045
+ return std::make_pair ( a, b );
2004
2046
}
2005
- T l;
2006
- T h;
2007
- ja[0 ].read ( l, true );
2008
- ja[1 ].read ( h, true );
2009
- return std::make_pair ( l, h );
2047
+ jv.throw_error ( " bad pair" );
2048
+ return std::make_pair ( a, b );
2010
2049
}
2011
2050
};
2012
2051
2052
+ // Loads pairs as objects { key1: t1, key2: t2 }, or just t1, which uses the default for the value of t2
2053
+ // e.g. named_pair_reader<string, int>( "a", "b", 3 ) loads
2054
+ // [ "1", { "a": "3", "b": 4 } ] -> ("1", 3), ("3", 4)
2013
2055
template <typename T1, typename T2>
2014
2056
class named_pair_reader : public generic_typed_reader <named_pair_reader<T1, T2>>
2015
2057
{
@@ -2033,7 +2075,7 @@ class named_pair_reader : public generic_typed_reader<named_pair_reader<T1, T2>>
2033
2075
jo.read ( key2, ret.second , true );
2034
2076
return ret;
2035
2077
}
2036
- // TODO: support pair format?
2078
+ // TODO: support pair format? Forward to pair_reader<T1, T2>?
2037
2079
if ( jv.test_array () ) {
2038
2080
jv.throw_error ( " invalid format" );
2039
2081
}
0 commit comments