Skip to content

Commit

Permalink
Support T? as expected field type
Browse files Browse the repository at this point in the history
  • Loading branch information
prakanth97 committed Mar 28, 2024
1 parent dcfd414 commit e09ccc3
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 9 deletions.
101 changes: 99 additions & 2 deletions ballerina/tests/from_json_string_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

import ballerina/test;

@test:Config {
dataProvider: basicTypeDataProviderForParseString
}
isolated function testJsonStringToBasicTypes(string sourceData, typedesc<anydata> expType,
isolated function testJsonStringToBasicTypes(string sourceData, typedesc<anydata> expType,
anydata expectedData) returns Error? {
anydata val1 = check parseString(sourceData, {}, expType);
test:assertEquals(val1, expectedData);
Expand Down Expand Up @@ -1380,6 +1379,104 @@ isolated function testUnalignedJsonContent() returns error? {
test:assertEquals(val.b, 1);
}

@test:Config
isolated function testNilableTypeAsFieldTypeForParseString() returns error? {
string jsonStr1 = string `
{
"id": 0,
"name": "Anne",
"address": {
"street": "Main",
"city": "94",
"id": 4294967295
}
}
`;
record {|
int? id;
string? name;
anydata? address;
|} val1 = check parseString(jsonStr1);
test:assertEquals(val1.id, 0);
test:assertEquals(val1.name, "Anne");
test:assertEquals(val1.address, {street: "Main", city: 94, id: 4294967295});

string jsonStr2 = string `{
"company": "wso2",
"employees": [
{
"name": "Walter White",
"age": 55
},
{
"name": "Jesse Pinkman",
"age": 25
}
]
}`;
record {|
anydata? company;
record {|
string name;
int age;
|}?[] employees;
|} val2 = check parseString(jsonStr2);
test:assertEquals(val2.company, "wso2");
test:assertEquals(val2.employees[0]?.name, "Walter White");
test:assertEquals(val2.employees[0]?.age, 55);
test:assertEquals(val2.employees[1]?.name, "Jesse Pinkman");
test:assertEquals(val2.employees[1]?.age, 25);


}

@test:Config
isolated function testNilableTypeAsFieldTypeForParseAsType() returns error? {
json jsonVal1 = {
"id": 0,
"name": "Anne",
"address": {
"street": "Main",
"city": 94,
"id": 4294967295
}
};
record {|
int? id;
string? name;
anydata? address;
|} val1 = check parseAsType(jsonVal1);
test:assertEquals(val1.id, 0);
test:assertEquals(val1.name, "Anne");
test:assertEquals(val1.address, {street: "Main", city: 94, id: 4294967295});

json jsonVal2 = {
"company": "wso2",
"employees": [
{
"name": "Walter White",
"age": 55
},
{
"name": "Jesse Pinkman",
"age": 25
}
]
};
record {|
anydata? company;
record {|
string name;
int age;
|}?[] employees;
|} val2 = check parseAsType(jsonVal2);
test:assertEquals(val2.company, "wso2");
test:assertEquals(val2.employees[0]?.name, "Walter White");
test:assertEquals(val2.employees[0]?.age, 55);
test:assertEquals(val2.employees[1]?.name, "Jesse Pinkman");
test:assertEquals(val2.employees[1]?.age, 25);
}

@test:Config
isolated function testParseStringNegative1() returns Error? {
string str = string `{
Expand Down
47 changes: 47 additions & 0 deletions ballerina/tests/from_json_with_projection_options_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,50 @@ isolated function testDisableOptionsOfProjectionTypeForParseAsType2() returns er
test:assertTrue(err is Error);
test:assertEquals((<Error>err).message(), "required field 'email' not present in JSON");
}

@test:Config {
groups: ["testFail"]
}
isolated function testAbsentAsNilableTypeAndAbsentAsNilableTypeForParseAsString2() returns error? {
record {|
string name;
|}|Error val1 = parseString(string `{"name": null}`, options3);
test:assertTrue(val1 is Error);
test:assertEquals((<Error>val1).message(), "incompatible value 'null' for type 'string' in field 'name'");

record {|
string? name;
|} val2 = check parseString(string `{"name": null}`, options3);
test:assertEquals(val2.name, ());

record {|
string name?;
|} val3 = check parseString(string `{"name": null}`, options3);
test:assertEquals(val3, {});

record {|
string? name?;
|} val4 = check parseString(string `{"name": null}`, options3);
test:assertEquals(val4?.name, ());

record {|
string name;
|}|Error val5 = parseString(string `{}`, options3);
test:assertTrue(val5 is Error);
test:assertEquals((<Error>val5).message(), "required field 'name' not present in JSON");

record {|
string? name;
|} val6 = check parseString(string `{}`, options3);
test:assertEquals(val6.name, ());

record {|
string name?;
|} val7 = check parseString(string `{}`, options3);
test:assertEquals(val7, {});

record {|
string? name?;
|} val8 = check parseString(string `{}`, options3);
test:assertEquals(val8?.name, ());
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public static Object fromStringWithType(BString string, Type expType) {
case TypeTags.UNION_TAG:
return stringToUnion(string, (UnionType) expType);
case TypeTags.JSON_TAG:
case TypeTags.ANYDATA_TAG:
return stringToUnion(string, JSON_TYPE_WITH_BASIC_TYPES);
case TypeTags.TYPE_REFERENCED_TYPE_TAG:
return fromStringWithType(string, ((ReferenceType) expType).getReferredType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,31 @@ static BMap<BString, Object> initRootMapValue(Type expectedType) {
}

static BArray initArrayValue(Type expectedType) {
return switch (expectedType.getTag()) {
case TypeTags.TUPLE_TAG -> ValueCreator.createTupleValue((TupleType) expectedType);
case TypeTags.ARRAY_TAG -> ValueCreator.createArrayValue((ArrayType) expectedType);
case TypeTags.JSON_TAG -> ValueCreator.createArrayValue(PredefinedTypes.TYPE_JSON_ARRAY);
case TypeTags.ANYDATA_TAG -> ValueCreator.createArrayValue(PredefinedTypes.TYPE_ANYDATA_ARRAY);
switch (expectedType.getTag()) {
case TypeTags.TUPLE_TAG -> {
return ValueCreator.createTupleValue((TupleType) expectedType);
}
case TypeTags.ARRAY_TAG -> {
return ValueCreator.createArrayValue((ArrayType) expectedType);
}
case TypeTags.JSON_TAG -> {
return ValueCreator.createArrayValue(PredefinedTypes.TYPE_JSON_ARRAY);
}
case TypeTags.ANYDATA_TAG -> {
return ValueCreator.createArrayValue(PredefinedTypes.TYPE_ANYDATA_ARRAY);
}
case TypeTags.UNION_TAG -> {
// Only handle T? cases.
// TODO: Support all union cases.
List<Type> filteredMembers = (((UnionType) expectedType).getMemberTypes()).stream().filter(
memType -> memType.getTag() != TypeTags.NULL_TAG).toList();
if (filteredMembers.size() == 1) {
return initArrayValue(filteredMembers.get(0));
}
throw DiagnosticLog.error(DiagnosticErrorCode.UNSUPPORTED_TYPE, expectedType);
}
default -> throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_TYPE, expectedType, "list type");
};
}
}

static Optional<BMap<BString, Object>> initNewMapValue(JsonParser.StateMachine sm) {
Expand Down Expand Up @@ -136,7 +154,16 @@ static BMap<BString, Object> checkTypeAndCreateMappingValue(JsonParser.StateMach
}
return checkTypeAndCreateMappingValue(sm, mutableType.get(), parentContext);
}
case TypeTags.UNION_TAG -> throw DiagnosticLog.error(DiagnosticErrorCode.UNSUPPORTED_TYPE, currentType);
case TypeTags.UNION_TAG -> {
// Only handle T? cases.
// TODO: Support all union cases.
List<Type> filteredMembers = (((UnionType) currentType).getMemberTypes()).stream().filter(
memType -> memType.getTag() != TypeTags.NULL_TAG).toList();
if (filteredMembers.size() == 1) {
return checkTypeAndCreateMappingValue(sm, filteredMembers.get(0), parentContext);
}
throw DiagnosticLog.error(DiagnosticErrorCode.UNSUPPORTED_TYPE, currentType);
}
default -> {
if (parentContext == JsonParser.StateMachine.ParserContext.ARRAY) {
throw DiagnosticLog.error(DiagnosticErrorCode.INVALID_TYPE, currentType, "map type");
Expand Down

0 comments on commit e09ccc3

Please sign in to comment.