Commit 9aa52315 authored by Branislav Katreniak's avatar Branislav Katreniak

json: implement decode(... input, DynamicStruct::Builder output)

parent c242a3a0
......@@ -183,6 +183,83 @@ KJ_TEST("encode union") {
KJ_EXPECT(json.encode(root) == "{\"before\":\"a\",\"middle\":44,\"bar\":321,\"after\":\"c\"}");
}
KJ_TEST("decode all types") {
JsonCodec json;
#define CASE(s, f) \
{ \
MallocMessageBuilder decodedMessage; \
auto root = decodedMessage.initRoot<TestAllTypes>(); \
json.decode(s, root); \
KJ_EXPECT((f)) \
}
CASE(R"({})", root.getBoolField() == false);
CASE(R"({"unknownField":7})", root.getBoolField() == false);
CASE(R"({"boolField":null})", root.getBoolField() == false);
CASE(R"({"boolField":true})", root.getBoolField() == true);
CASE(R"({"int8Field":7})", root.getInt8Field() == 7);
CASE(R"({"int8Field":"7"})", root.getInt8Field() == 7);
CASE(R"({"int16Field":7})", root.getInt16Field() == 7);
CASE(R"({"int16Field":"7"})", root.getInt16Field() == 7);
CASE(R"({"int32Field":7})", root.getInt32Field() == 7);
CASE(R"({"int32Field":"7"})", root.getInt32Field() == 7);
CASE(R"({"int64Field":7})", root.getInt64Field() == 7);
CASE(R"({"int64Field":"7"})", root.getInt64Field() == 7);
CASE(R"({"uInt8Field":7})", root.getUInt8Field() == 7);
CASE(R"({"uInt8Field":"7"})", root.getUInt8Field() == 7);
CASE(R"({"uInt16Field":7})", root.getUInt16Field() == 7);
CASE(R"({"uInt16Field":"7"})", root.getUInt16Field() == 7);
CASE(R"({"uInt32Field":7})", root.getUInt32Field() == 7);
CASE(R"({"uInt32Field":"7"})", root.getUInt32Field() == 7);
CASE(R"({"uInt64Field":7})", root.getUInt64Field() == 7);
CASE(R"({"uInt64Field":"7"})", root.getUInt64Field() == 7);
CASE(R"({"textField":"hello"})", kj::str("hello") == root.getTextField());
CASE(R"({"dataField":[7,0,122]})",
kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataField());
CASE(R"({"structField":null})", root.hasStructField() == false);
CASE(R"({"structField":{}})", root.hasStructField() == true);
CASE(R"({"structField":{}})", root.getStructField().getBoolField() == false);
CASE(R"({"structField":{"boolField":false}})", root.getStructField().getBoolField() == false);
CASE(R"({"structField":{"boolField":true}})", root.getStructField().getBoolField() == true);
CASE(R"({"enumField":"bar"})", root.getEnumField() == TestEnum::BAR);
CASE(R"({})", root.hasBoolList() == false);
CASE(R"({"boolList":null})", root.hasBoolList() == false);
CASE(R"({"boolList":[]})", root.hasBoolList() == true);
CASE(R"({"boolList":[]})", root.getBoolList().size() == 0);
CASE(R"({"boolList":[null]})", root.getBoolList().size() == 1);
CASE(R"({"boolList":[null]})", root.getBoolList()[0] == false);
CASE(R"({"boolList":[false]})", root.getBoolList()[0] == false);
CASE(R"({"boolList":[true]})", root.getBoolList()[0] == true);
CASE(R"({"int8List":[7]})", root.getInt8List()[0] == 7);
CASE(R"({"int8List":["7"]})", root.getInt8List()[0] == 7);
CASE(R"({"int16List":[7]})", root.getInt16List()[0] == 7);
CASE(R"({"int16List":["7"]})", root.getInt16List()[0] == 7);
CASE(R"({"int32List":[7]})", root.getInt32List()[0] == 7);
CASE(R"({"int32List":["7"]})", root.getInt32List()[0] == 7);
CASE(R"({"int64List":[7]})", root.getInt64List()[0] == 7);
CASE(R"({"int64List":["7"]})", root.getInt64List()[0] == 7);
CASE(R"({"uInt8List":[7]})", root.getUInt8List()[0] == 7);
CASE(R"({"uInt8List":["7"]})", root.getUInt8List()[0] == 7);
CASE(R"({"uInt16List":[7]})", root.getUInt16List()[0] == 7);
CASE(R"({"uInt16List":["7"]})", root.getUInt16List()[0] == 7);
CASE(R"({"uInt32List":[7]})", root.getUInt32List()[0] == 7);
CASE(R"({"uInt32List":["7"]})", root.getUInt32List()[0] == 7);
CASE(R"({"uInt64List":[7]})", root.getUInt64List()[0] == 7);
CASE(R"({"uInt64List":["7"]})", root.getUInt64List()[0] == 7);
CASE(R"({"textList":["hello"]})", kj::str("hello") == root.getTextList()[0]);
CASE(R"({"dataList":[[7,0,122]]})",
kj::heapArray<byte>({7,0,122}).asPtr() == root.getDataList()[0]);
CASE(R"({"structList":null})", root.hasStructList() == false);
CASE(R"({"structList":[null]})", root.hasStructList() == true);
CASE(R"({"structList":[null]})", root.getStructList()[0].getBoolField() == false);
CASE(R"({"structList":[{}]})", root.getStructList()[0].getBoolField() == false);
CASE(R"({"structList":[{"boolField":false}]})", root.getStructList()[0].getBoolField() == false);
CASE(R"({"structList":[{"boolField":true}]})", root.getStructList()[0].getBoolField() == true);
CASE(R"({"enumList":["bar"]})", root.getEnumList()[0] == TestEnum::BAR);
#undef CASE
}
KJ_TEST("basic json decoding") {
// TODO(cleanup): this test is a mess!
JsonCodec json;
......
......@@ -379,13 +379,119 @@ void JsonCodec::encodeField(StructSchema::Field field, DynamicValue::Reader inpu
encode(input, field.getType(), output);
}
namespace {
int64_t parseInt64(kj::StringPtr s)
{
char *endPtr;
errno = 0;
int64_t value = std::strtoll(s.begin(), &endPtr, 10);
KJ_REQUIRE(endPtr == s.end() && errno != ERANGE, "String is not correct int64 number");
return value;
}
uint64_t parseUInt64(kj::StringPtr s)
{
char *endPtr;
errno = 0;
uint64_t value = std::strtoull(s.begin(), &endPtr, 10);
KJ_ASSERT(endPtr == s.end() && errno != ERANGE, "String is not correct uint64 number");
return value;
}
template <typename Set, typename DecodeArray, typename DecodeObject>
void decodeField(Type type, JsonValue::Reader value, Set set, DecodeArray decodeArray,
DecodeObject decodeObject) {
switch (value.which()) {
case JsonValue::NULL_:
break;
case JsonValue::BOOLEAN:
set(value.getBoolean());
break;
case JsonValue::NUMBER:
set(value.getNumber());
break;
case JsonValue::STRING:
if (type.isInt8() || type.isInt16() || type.isInt32() || type.isInt64()) {
set(parseInt64(value.getString()));
} else if (type.isUInt8() || type.isUInt16() || type.isUInt32() || type.isUInt64()) {
set(parseUInt64(value.getString()));
} else {
set(value.getString());
}
break;
case JsonValue::ARRAY: {
if (type.isData()) {
kj::Vector<byte> data;
for (auto arrayObject : value.getArray()) {
auto x = int(arrayObject.getNumber());
KJ_REQUIRE(x >= 0 && x <= 255, "Number in array of bytes out of range.");
data.add(byte(x));
}
set(Data::Reader(data.asPtr()));
} else {
decodeArray(value.getArray());
}
break;
}
case JsonValue::OBJECT:
decodeObject(value.getObject());
break;
case JsonValue::CALL:
//TODO(soon)
KJ_FAIL_ASSERT("JSON call decode not implemented yet. :(");
break;
}
}
} //namespace
void JsonCodec::decodeArray(List<JsonValue>::Reader input, DynamicList::Builder output) const {
KJ_ASSERT(input.size() == output.size(), "Builder must be initialized to input size");
auto type = output.getSchema().getElementType();
for (auto i = 0; i < input.size(); i++) {
decodeField(type, input[i],
[&](DynamicValue::Reader value) { output.set(i, value); },
[&](List<JsonValue>::Reader array) {
decodeArray(array, output.init(i, array.size()).as<DynamicList>());
},
[&](List<JsonValue::Field>::Reader object) {
decodeObject(object, output[i].as<DynamicStruct>());
});
}
}
void JsonCodec::decodeObject(List<JsonValue::Field>::Reader input, DynamicStruct::Builder output)
const {
for (auto field : input) {
KJ_IF_MAYBE(fieldSchema, output.getSchema().findFieldByName(field.getName())) {
decodeField((*fieldSchema).getType(), field.getValue(),
[&](DynamicValue::Reader value) { output.set(*fieldSchema, value); },
[&](List<JsonValue>::Reader array) {
decodeArray(array, output.init(*fieldSchema, array.size()).as<DynamicList>());
},
[&](List<JsonValue::Field>::Reader object) {
decodeObject(object, output.init(*fieldSchema).as<DynamicStruct>());
});
} else {
//Unknown json fields are ignored to allow schema evolution
}
}
}
void JsonCodec::decode(JsonValue::Reader input, DynamicStruct::Builder output) const {
KJ_FAIL_ASSERT("JSON decode not implement yet. :(");
// TODO(soon): type and field handlers
switch (input.which()) {
case JsonValue::OBJECT:
decodeObject(input.getObject(), output);
break;
default:
KJ_FAIL_REQUIRE("Top level json value must be object");
};
}
Orphan<DynamicValue> JsonCodec::decode(
JsonValue::Reader input, Type type, Orphanage orphanage) const {
KJ_FAIL_ASSERT("JSON decode not implement yet. :(");
//TODO(soon)
KJ_FAIL_ASSERT("JSON decode into orphanage not implement yet. :(");
}
// -----------------------------------------------------------------------------
......
......@@ -201,6 +201,8 @@ private:
void encodeField(StructSchema::Field field, DynamicValue::Reader input,
JsonValue::Builder output) const;
void decodeArray(List<JsonValue>::Reader input, DynamicList::Builder output) const;
void decodeObject(List<JsonValue::Field>::Reader input, DynamicStruct::Builder output) const;
void addTypeHandlerImpl(Type type, HandlerBase& handler);
void addFieldHandlerImpl(StructSchema::Field field, Type type, HandlerBase& handler);
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment