Support table deserialization in from_json()

This allows additional data roundtripping through JSON since to_json() already
supports tables. There are some subtleties around the formatting of strings in
JSON object keys, for which this adds a bit of helper infrastructure.

This also expands the language.table test to verify the roundtrips, and adapts
bif.from_json to include a table in the test record.
This commit is contained in:
Christian Kreibich 2024-06-28 15:24:52 -07:00
parent df645e9bb2
commit 92c1098e97
6 changed files with 148 additions and 64 deletions

View file

@ -330,7 +330,7 @@ TableValPtr Val::GetRecordFields() {
}
// A predicate to identify those types we render as a string in JSON.
static bool IsQuotedJSONType(const TypePtr& t) {
static bool UsesJSONStringType(const TypePtr& t) {
if ( t == nullptr )
return false;
@ -440,8 +440,10 @@ static void BuildJSON(json::detail::NullDoubleWriter& writer, Val* val, bool onl
BuildJSON(key_writer, entry_key, only_loggable, re);
string key_str = buffer.GetString();
if ( key_str.length() >= 2 && key_str[0] == '"' && key_str[key_str.length() - 1] == '"' )
// Strip quotes.
// Strip the quotes for any type we render as a string. This
// makes the JSON object's keys look more natural, yielding
// '{ "foo": ... }', not '{ "\"foo\"": ... }', for such types.
if ( UsesJSONStringType(entry_key->GetType()) )
key_str = key_str.substr(1, key_str.length() - 2);
BuildJSON(writer, entry->GetVal().get(), only_loggable, re, key_str);
@ -1064,34 +1066,69 @@ static std::variant<ValPtr, std::string> BuildVal(const rapidjson::Value& j, con
}
case TYPE_TABLE: {
if ( ! j.IsArray() )
return mismatch_err();
if ( ! t->IsSet() )
return util::fmt("tables are not supported");
auto tt = t->AsSetType();
auto tl = tt->GetIndices();
auto tt = t->AsTableType(); // The table vs set type does not matter below
auto tv = make_intrusive<TableVal>(IntrusivePtr{NewRef{}, tt});
auto tl = tt->GetIndices();
for ( const auto& item : j.GetArray() ) {
std::variant<ValPtr, std::string> v;
if ( t->IsSet() ) {
if ( ! j.IsArray() )
return mismatch_err();
if ( tl->GetTypes().size() == 1 )
v = BuildVal(item, tl->GetPureType(), key_func);
else
v = BuildVal(item, tl, key_func);
for ( const auto& item : j.GetArray() ) {
std::variant<ValPtr, std::string> v;
if ( ! get_if<ValPtr>(&v) )
return v;
if ( tl->GetTypes().size() == 1 )
v = BuildVal(item, tl->GetPureType(), key_func);
else
v = BuildVal(item, tl, key_func);
if ( ! std::get<ValPtr>(v) )
continue;
if ( ! get_if<ValPtr>(&v) )
return v;
if ( ! std::get<ValPtr>(v) )
continue;
tv->Assign(std::move(std::get<ValPtr>(v)), nullptr);
tv->Assign(std::move(std::get<ValPtr>(v)), nullptr);
}
return tv;
}
else {
if ( ! j.IsObject() )
return mismatch_err();
return tv;
for ( auto it = j.MemberBegin(); it != j.MemberEnd(); ++it ) {
rapidjson::Document idxstr;
idxstr.Parse(it->name.GetString(), it->name.GetStringLength());
std::variant<ValPtr, std::string> idx;
if ( tl->GetTypes().size() > 1 )
idx = BuildVal(idxstr, tl, key_func);
else if ( UsesJSONStringType(tl->GetPureType()) )
// Parse this with the quotes the string came with. This
// mirrors the quote-stripping in BuildJSON().
idx = BuildVal(it->name, tl->GetPureType(), key_func);
else
// Parse the string's content, not the full JSON string.
idx = BuildVal(idxstr, tl->GetPureType(), key_func);
if ( ! get_if<ValPtr>(&idx) )
return idx;
if ( ! std::get<ValPtr>(idx) )
continue;
auto v = BuildVal(it->value, tt->Yield(), key_func);
if ( ! get_if<ValPtr>(&v) )
return v;
if ( ! std::get<ValPtr>(v) )
continue;
tv->Assign(std::move(std::get<ValPtr>(idx)), std::move(std::get<ValPtr>(v)));
}
return tv;
}
}
case TYPE_RECORD: {