diff --git a/compiler2/record.c b/compiler2/record.c index 6331a10b6a679cbdb7afa9a2fe2bbe99d8c54e63..c3ae41ffeb8b7ecef4dccfe0e3eef890f1fc0814 100644 --- a/compiler2/record.c +++ b/compiler2/record.c @@ -3219,7 +3219,16 @@ char* generate_json_decoder(char* src, const struct_def* sdef) boolean has_metainfo_enabled = FALSE; for (int i = 0; i < sdef->nElements; ++i) { - src = mputprintf(src, " boolean %s_found = FALSE;\n", sdef->elements[i].name); + if (sdef->elements[i].jsonDefaultValue) { + // initialize fields with their default values (they will be overwritten + // later, if the JSON document contains data for these fields) + src = mputprintf(src, + " field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n" + , sdef->elements[i].name, sdef->elements[i].typedescrname); + } + else { + src = mputprintf(src, " boolean %s_found = FALSE;\n", sdef->elements[i].name); + } if (sdef->elements[i].jsonMetainfoUnbound) { // initialize meta info states src = mputprintf(src, @@ -3259,10 +3268,12 @@ char* generate_json_decoder(char* src, const struct_def* sdef) src = mputprintf(src, // check field name "if (%d == name_len && 0 == strncmp(fld_name, \"%s\", name_len)) {\n" - " %s_found = TRUE;\n" , (int)strlen(sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname) - , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname - , sdef->elements[i].name); + , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); + if (!sdef->elements[i].jsonDefaultValue) { + src = mputprintf(src, + " %s_found = TRUE;\n", sdef->elements[i].name); + } if (has_metainfo_enabled) { src = mputstr(src, " if (is_metainfo) {\n"); if (sdef->elements[i].jsonMetainfoUnbound) { @@ -3416,59 +3427,60 @@ char* generate_json_decoder(char* src, const struct_def* sdef) , (unsigned long) strlen(sdef->elements[i].dispname) , sdef->elements[i].dispname); } - src = mputprintf(src, - "if (!%s_found) {\n" - , sdef->elements[i].name); - if (sdef->elements[i].jsonDefaultValue) { + if (!sdef->elements[i].jsonDefaultValue) { src = mputprintf(src, - " field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n" - , sdef->elements[i].name, sdef->elements[i].typedescrname); - } - else if (sdef->elements[i].isOptional) { - // if the conditions in attribute 'choice' indicate that this field is - // mandatory, then display an error - if (sdef->elements[i].jsonChosen != NULL) { - int j; - boolean has_otherwise = FALSE; - for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { - if (sdef->elements[i].jsonChosen->list[j].nElements == 0) { - has_otherwise = TRUE; - break; - } - } - boolean first_found = FALSE; - for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { - if ((!has_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum != -2) || - (has_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum == -2)) { - if (!first_found) { - src = mputstr(src, " if ("); - first_found = TRUE; + "if (!%s_found) {\n" + , sdef->elements[i].name); + if (sdef->elements[i].isOptional) { + // if the conditions in attribute 'choice' indicate that this field is + // mandatory, then display an error + if (sdef->elements[i].jsonChosen != NULL) { + int j; + boolean has_otherwise = FALSE; + boolean omit_otherwise = FALSE; + for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { + if (sdef->elements[i].jsonChosen->list[j].nElements == 0) { + has_otherwise = TRUE; + if (sdef->elements[i].jsonChosen->list[j].fieldnum == -2) { + omit_otherwise = TRUE; + } + break; } - else { - src = mputstr(src, "\n || "); + } + boolean first_found = FALSE; + for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) { + if (((!has_otherwise || omit_otherwise) && sdef->elements[i].jsonChosen->list[j].fieldnum != -2) || + (has_otherwise && !omit_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum == -2)) { + if (!first_found) { + src = mputstr(src, " if ("); + first_found = TRUE; + } + else { + src = mputstr(src, "\n || "); + } + src = genRawFieldChecker(src, sdef->elements[i].jsonChosen->list + j, !has_otherwise || omit_otherwise); } - src = genRawFieldChecker(src, sdef->elements[i].jsonChosen->list + j, !has_otherwise); + } + if (first_found) { + src = mputprintf(src, + ") {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED, \"%s\");\n" + " return JSON_ERROR_FATAL;\n" + " }\n", sdef->elements[i].dispname); } } - if (first_found) { - src = mputprintf(src, - ") {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED, \"%s\");\n" - " return JSON_ERROR_FATAL;\n" - " }\n", sdef->elements[i].dispname); - } + src = mputprintf(src, + " field_%s = OMIT_VALUE;\n" + , sdef->elements[i].name); + } else { + src = mputprintf(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_MISSING_FIELD_ERROR, \"%s\");\n" + " return JSON_ERROR_FATAL;\n" + , sdef->elements[i].dispname); } - src = mputprintf(src, - " field_%s = OMIT_VALUE;\n" - , sdef->elements[i].name); - } else { - src = mputprintf(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_MISSING_FIELD_ERROR, \"%s\");\n" - " return JSON_ERROR_FATAL;\n" - , sdef->elements[i].dispname); - } - src = mputstr(src, - " }\n "); + src = mputstr(src, + " }\n "); + } // if there's no default value } src = mputstr(src, "\n return (int)dec_len;\n"); diff --git a/regression_test/json/AttributeTestcases.ttcn b/regression_test/json/AttributeTestcases.ttcn index 1b6141fff5dcc81c2a6b0e999477cede6a6d29df..ab87a95b7576a0d3c38135059661c2149d72d70a 100644 --- a/regression_test/json/AttributeTestcases.ttcn +++ b/regression_test/json/AttributeTestcases.ttcn @@ -627,6 +627,54 @@ testcase tc_attribute_chosen_negtest() runs on MTC { setverdict(pass); } +// Testing JSON decoding with attributes 'chosen' and 'default'. +// (The conditions in the attribute 'chosen' depend on fields that have default values.) +testcase tc_attribute_chosen_default() runs on MTC { + // Test #1: both fields use their default values + var octetstring buff := char2oct("{\"field\":{\"num\":1,\"str\":\"abc\"}}"); + var PduWithId2 dec_exp := { protocolId1 := 1, protocolId2 := 0, field := { type1 := { num := 1, str := "abc" } } }; + var PduWithId2 dec := f_dec_pduwithid2(buff); + if (dec != dec_exp) { + setverdict(fail, "Test #1 failed. Expected: ", dec_exp, ", got: ", dec); + } + + // Test #2: the first field uses its default value, the second decodes its value from the buffer + buff := char2oct("{\"protocolId2\":-1,\"field\":{\"num\":1,\"str\":\"abc\"}}"); + dec_exp := { protocolId1 := 1, protocolId2 := -1, field := { type1 := { num := 1, str := "abc" } } }; + dec := f_dec_pduwithid2(buff); + if (dec != dec_exp) { + setverdict(fail, "Test #2 failed. Expected: ", dec_exp, ", got: ", dec); + } + + // Test #3: the second field uses its default value, the first decodes its value from the buffer + buff := char2oct("{\"protocolId1\":6,\"field\":{\"num\":1,\"str\":\"abc\"}}"); + dec_exp := { protocolId1 := 6, protocolId2 := 0, field := { type3 := { num := 1.0, str := "abc" } } }; + dec := f_dec_pduwithid2(buff); + if (dec != dec_exp) { + setverdict(fail, "Test #3 failed. Expected: ", dec_exp, ", got: ", dec); + } + + // Test #4: both fields decode their values from the buffer + buff := char2oct("{\"protocolId1\":6,\"protocolId2\":-1}"); + dec_exp := { protocolId1 := 6, protocolId2 := -1, field := omit }; + dec := f_dec_pduwithid2(buff); + if (dec != dec_exp) { + setverdict(fail, "Test #4 failed. Expected: ", dec_exp, ", got: ", dec); + } + + // Test #5: both fields decode their values from the buffer, + // however, the first field appears after the union field in the buffer, + // so its default value is used when checking the conditions of the attribute 'chosen' + buff := char2oct("{\"protocolId2\":-1,\"field\":{\"num\":1,\"str\":\"abc\"},\"protocolId1\":6}"); + dec_exp := { protocolId1 := 6, protocolId2 := -1, field := { type1 := { num := 1, str := "abc" } } }; + dec := f_dec_pduwithid2(buff); + if (dec != dec_exp) { + setverdict(fail, "Test #5 failed. Expected: ", dec_exp, ", got: ", dec); + } + + setverdict(pass); +} + control { execute(tc_NoAttributeOnUpperLevel()) @@ -655,5 +703,6 @@ control { execute(tc_attribute_default_struct()); execute(tc_attribute_chosen()); execute(tc_attribute_chosen_negtest()); + execute(tc_attribute_chosen_default()); } } diff --git a/regression_test/json/Functions.ttcn b/regression_test/json/Functions.ttcn index edb639f0896bb5306ff337e4a8397c738a15f1c0..b764eff2316db619af5181eba8451a6e7a60d051 100644 --- a/regression_test/json/Functions.ttcn +++ b/regression_test/json/Functions.ttcn @@ -430,6 +430,9 @@ external function f_dec_null(in octetstring x) return HasNull external function f_dec_pduwithid(in octetstring x) return PduWithId with { extension "prototype(convert) decode(JSON)" } +external function f_dec_pduwithid2(in octetstring x) return PduWithId2 + with { extension "prototype(convert) decode(JSON)" } + //============== Internal Functions ==================== function f_check_encoding(in octetstring encoded, in octetstring expected) { diff --git a/regression_test/json/Types.ttcn b/regression_test/json/Types.ttcn index 5ce1cd3d65cf2f22a607c897eb48b773d35436d7..e1e0eca094aaa031655f67555390508f7622699e 100644 --- a/regression_test/json/Types.ttcn +++ b/regression_test/json/Types.ttcn @@ -351,6 +351,17 @@ type record StructType3 { type record of PduWithId PduWithIdList; +type record PduWithId2 { + integer protocolId1, + integer protocolId2, + Choices field optional +} +with { + variant (field) "chosen (type1, { protocolId1 = 1, protocolId2 = 1 }; type2, protocolId1 = 0; type3, protocolId2 = 0; omit, otherwise)"; + variant (protocolId1) "default (1)"; + variant (protocolId2) "default (0)"; +} + } with { encode "JSON"; extension "anytype integer, charstring, R, RoI, Thing"; diff --git a/usrguide/referenceguide.doc b/usrguide/referenceguide.doc index 40f6d4b2fb2e9988ee9bf1514dca8905303f138c..9468b943a56d4ed8cdf60000bbe537ad15c6e4bb 100644 Binary files a/usrguide/referenceguide.doc and b/usrguide/referenceguide.doc differ