diff --git a/compiler2/Type.cc b/compiler2/Type.cc index cdb774e41af243d50eaaf74ce3bf1a68e6e55c5c..5cd91e85218837ea0e8524335bd06634e964c2c8 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -2780,8 +2780,22 @@ namespace Common { "field of a record or set."); } - if (jsonattrib->as_value && T_CHOICE_T != get_type_refd_last()->typetype) { - error("Invalid attribute, 'as value' is only allowed for unions"); + if (jsonattrib->as_value) { + Type* last = get_type_refd_last(); + switch (last->typetype) { + case T_CHOICE_T: + case T_ANYTYPE: + break; // OK + case T_SEQ_T: + case T_SET_T: + if (last->get_nof_comps() == 1) { + break; // OK + } + // else fall through + default: + error("Invalid attribute, 'as value' is only allowed for unions, " + "the anytype, or records or sets with one field"); + } } if (NULL != jsonattrib->alias) { @@ -2792,9 +2806,34 @@ namespace Common { "record, set or union."); } if (NULL != parent && NULL != parent->jsonattrib && - T_CHOICE_T == parent->typetype && parent->jsonattrib->as_value) { - warning("Attribute 'name as ...' will be ignored, because parent union " - "is encoded without field names."); + parent->jsonattrib->as_value) { + const char* parent_type_name = NULL; + switch (parent->typetype) { + case T_SEQ_T: + if (parent->get_nof_comps() == 1) { + parent_type_name = "record"; + } + break; + case T_SET_T: + if (parent->get_nof_comps() == 1) { + parent_type_name = "set"; + } + break; + case T_CHOICE_T: + parent_type_name = "union"; + break; + case T_ANYTYPE: + parent_type_name = "anytype"; + break; + default: + break; + } + if (parent_type_name != NULL) { + // parent_type_name remains null if the 'as value' attribute is set + // for an invalid type + warning("Attribute 'name as ...' will be ignored, because parent %s " + "is encoded without field names.", parent_type_name); + } } } @@ -2846,24 +2885,29 @@ namespace Common { delete[] checked_keywords; } if (jsonattrib->metainfo_unbound) { + Type* last = get_type_refd_last(); Type* parent = get_parent_type(); - if (T_SEQ_T == get_type_refd_last()->typetype || - T_SET_T == get_type_refd_last()->typetype) { + if (T_SEQ_T == last->typetype || T_SET_T == last->typetype) { // if it's set for the record/set, pass it onto its fields - size_t nof_comps = get_nof_comps(); - for (size_t i = 0; i < nof_comps; i++) { - Type* comp_type = get_comp_byIndex(i)->get_type(); - if (NULL == comp_type->jsonattrib) { - comp_type->jsonattrib = new JsonAST; + size_t nof_comps = last->get_nof_comps(); + if (jsonattrib->as_value && nof_comps == 1) { + warning("Attribute 'metainfo for unbound' will be ignored, because " + "the %s is encoded without field names.", + T_SEQ_T == last->typetype ? "record" : "set"); + } + else { + for (size_t i = 0; i < nof_comps; i++) { + Type* comp_type = last->get_comp_byIndex(i)->get_type(); + if (NULL == comp_type->jsonattrib) { + comp_type->jsonattrib = new JsonAST; + } + comp_type->jsonattrib->metainfo_unbound = true; } - comp_type->jsonattrib->metainfo_unbound = true; } } - else if (T_SEQOF != get_type_refd_last()->typetype && - T_SETOF != get_type_refd_last()->typetype && - T_ARRAY != get_type_refd_last()->typetype && - (NULL == parent || (T_SEQ_T != parent->typetype && - T_SET_T != parent->typetype))) { + else if (T_SEQOF != last->typetype && T_SETOF != last->typetype && + T_ARRAY != last->typetype && (NULL == parent || + (T_SEQ_T != parent->typetype && T_SET_T != parent->typetype))) { // only allowed if it's an array type or a field of a record/set error("Invalid attribute 'metainfo for unbound', requires record, set, " "record of, set of, array or field of a record or set"); diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 0e7fd7ce97b5fa4ccffcd0b4bcd75d7673e63d0b..91bba9d300b02de9fe456497b40327a7d37a8c00 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -1207,7 +1207,7 @@ void Type::generate_code_Choice(output_struct *target) sdef.elements[i].xerUseUnion = cftype->xerattrib->useUnion_; } } - if (sdef.jsonAsValue) { + if (sdef.hasJson && enable_json()) { // Determine the JSON value type of each field to make decoding faster typetype_t tt = cftype->get_type_refd_last()->typetype; switch(tt) { @@ -1253,11 +1253,16 @@ void Type::generate_code_Choice(output_struct *target) case T_SEQ_A: case T_SET_T: case T_SET_A: + if (cftype->get_type_refd_last()->get_nof_comps() > 1) { + sdef.elements[i].jsonValueType = JSON_OBJECT; + break; + } + // else fall through case T_CHOICE_T: case T_CHOICE_A: case T_ANYTYPE: case T_OPENTYPE: - sdef.elements[i].jsonValueType = JSON_OBJECT; + sdef.elements[i].jsonValueType = JSON_ANY_VALUE; break; case T_SEQOF: case T_SETOF: @@ -1270,10 +1275,6 @@ void Type::generate_code_Choice(output_struct *target) } if (cftype->jsonattrib) { sdef.elements[i].jsonAlias = cftype->jsonattrib->alias; - if (sdef.jsonAsValue && cftype->jsonattrib->as_value) { - // Override the JSON_OBJECT value given in the switch - sdef.elements[i].jsonValueType = JSON_ANY_VALUE; - } } } if(rawattrib) { diff --git a/compiler2/datatypes.h b/compiler2/datatypes.h index 95f376e77b59883e197c7314b0ee0e7288cc6c41..19da16ceef0360b60e7eebb4ded5a89cb1dcffdd 100644 --- a/compiler2/datatypes.h +++ b/compiler2/datatypes.h @@ -112,7 +112,7 @@ typedef struct { boolean xerUseUnion; /* for choice */ boolean xerHasNamespaces; /* from the module */ boolean xerEmbedValuesPossible; /* for sequence */ - boolean jsonAsValue; /* for choice */ + boolean jsonAsValue; /* for both */ /** The index of the last field which can generate empty XML, or -1 */ int exerMaybeEmptyIndex; /* for union */ const char * control_ns_prefix; diff --git a/compiler2/record.c b/compiler2/record.c index 4b1c131d186846b9d47151dc4034994a7cab6ad8..27bd00cfa5138f2823d75126511dfab3b2111824 100644 --- a/compiler2/record.c +++ b/compiler2/record.c @@ -4373,237 +4373,262 @@ void defRecordClass1(const struct_def *sdef, output_struct *output) if (json_needed) { // JSON encode, RT1 src = mputprintf(src, - "int %s::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) const\n" + "int %s::JSON_encode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok) const\n" "{\n" " if (!is_bound()) {\n" " TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n" " \"Encoding an unbound value of type %s.\");\n" " return -1;\n" - " }\n\n" - " int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n\n" - , name, dispname); - for (i = 0; i < sdef->nElements; ++i) { - if (sdef->elements[i].isOptional && !sdef->elements[i].jsonOmitAsNull && - !sdef->elements[i].jsonMetainfoUnbound) { - src = mputprintf(src, - " if (field_%s.is_present())\n" - , sdef->elements[i].name); + " }\n\n", name, !sdef->jsonAsValue ? " p_td" : "", dispname); + if (sdef->nElements == 1) { + if (!sdef->jsonAsValue) { + src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n"); } - src = mputprintf(src, - " {\n" - " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n " - , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); - if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, " %sreturn field_%s.JSON_encode(%s_descr_, p_tok);\n", + sdef->jsonAsValue ? "" : " ", sdef->elements[0].name, sdef->elements[0].typedescrname); + if (!sdef->jsonAsValue) { + src = mputstr(src, " }\n"); + } + } + if (!sdef->jsonAsValue) { + src = mputstr(src, " int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (sdef->elements[i].isOptional && !sdef->elements[i].jsonOmitAsNull && + !sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, + " if (field_%s.is_present())\n" + , sdef->elements[i].name); + } src = mputprintf(src, - "if (!field_%s.is_bound()) {\n" - " enc_len += p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL);\n" - " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"metainfo %s\");\n" - " enc_len += p_tok.put_next_token(JSON_TOKEN_STRING, \"\\\"unbound\\\"\");\n" - " }\n" - " else " - , sdef->elements[i].name + " {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n " , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); + if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, + "if (!field_%s.is_bound()) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL);\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"metainfo %s\");\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_STRING, \"\\\"unbound\\\"\");\n" + " }\n" + " else " + , sdef->elements[i].name + , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); + } + src = mputprintf(src, + "enc_len += field_%s.JSON_encode(%s_descr_, p_tok);\n" + " }\n\n" + , sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputprintf(src, - "enc_len += field_%s.JSON_encode(%s_descr_, p_tok);\n" - " }\n\n" - , sdef->elements[i].name, sdef->elements[i].typedescrname); + src = mputstr(src, + " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n" + " return enc_len;\n"); } - src = mputstr(src, - " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n" - " return enc_len;\n" - "}\n\n"); + src = mputstr(src, "}\n\n"); // JSON decode, RT1 src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent)\n" - "{\n" - " json_token_t j_token = JSON_TOKEN_NONE;\n" - " size_t dec_len = p_tok.get_next_token(&j_token, NULL, NULL);\n" - " if (JSON_TOKEN_ERROR == j_token) {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " else if (JSON_TOKEN_OBJECT_START != j_token) {\n" - " return JSON_ERROR_INVALID_TOKEN;\n" - " }\n" - , name); - boolean has_metainfo_enabled = FALSE; - for (i = 0; i < sdef->nElements; ++i) { - src = mputprintf(src, " boolean %s_found = FALSE;\n", sdef->elements[i].name); - if (sdef->elements[i].jsonMetainfoUnbound) { - // initialize meta info states - src = mputprintf(src, - " int metainfo_%s = JSON_METAINFO_NONE;\n" - , sdef->elements[i].name); - has_metainfo_enabled = TRUE; + "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "{\n", name, !sdef->jsonAsValue ? " p_td" : ""); + + if (sdef->nElements == 1) { + if (!sdef->jsonAsValue) { + src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n"); + } + src = mputprintf(src, " %sreturn field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n", + sdef->jsonAsValue ? "" : " ", sdef->elements[0].name, sdef->elements[0].typedescrname); + if (!sdef->jsonAsValue) { + src = mputstr(src, " }\n"); } } - src = mputstr(src, - // Read name - value token pairs until we reach some other token - "\n while (TRUE) {\n" - " char* fld_name = 0;\n" - " size_t name_len = 0;\n" - " size_t buf_pos = p_tok.get_buf_pos();\n" - " dec_len += p_tok.get_next_token(&j_token, &fld_name, &name_len);\n" - " if (JSON_TOKEN_ERROR == j_token) {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_NAME_TOKEN_ERROR);\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - // undo the last action on the buffer - " else if (JSON_TOKEN_NAME != j_token) {\n" - " p_tok.set_buf_pos(buf_pos);\n" - " break;\n" - " }\n" - " else {\n "); - if (has_metainfo_enabled) { - // check for meta info + if (!sdef->jsonAsValue) { src = mputstr(src, - "boolean is_metainfo = FALSE;\n" - " if (name_len > 9 && 0 == strncmp(fld_name, \"metainfo \", 9)) {\n" - " fld_name += 9;\n" - " name_len -= 9;\n" - " is_metainfo = TRUE;\n" - " }\n "); - } - for (i = 0; i < sdef->nElements; ++i) { - 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); - if (has_metainfo_enabled) { - src = mputstr(src, " if (is_metainfo) {\n"); - if (sdef->elements[i].jsonMetainfoUnbound) { - src = mputprintf(src, - // check meta info - " char* info_value = 0;\n" - " size_t info_len = 0;\n" - " dec_len += p_tok.get_next_token(&j_token, &info_value, &info_len);\n" - " if (JSON_TOKEN_STRING == j_token && 9 == info_len &&\n" - " 0 == strncmp(info_value, \"\\\"unbound\\\"\", 9)) {\n" - " metainfo_%s = JSON_METAINFO_UNBOUND;\n" - " }\n" - " else {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_VALUE_ERROR, \"%s\");\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - , sdef->elements[i].name, sdef->elements[i].dispname); - } - else { - src = mputprintf(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_NOT_APPLICABLE, \"%s\");\n" - " return JSON_ERROR_FATAL;\n" - , sdef->elements[i].dispname); - } - src = mputstr(src, - " }\n" - " else {\n"); + " json_token_t j_token = JSON_TOKEN_NONE;\n" + " size_t dec_len = p_tok.get_next_token(&j_token, NULL, NULL);\n" + " if (JSON_TOKEN_ERROR == j_token) {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " else if (JSON_TOKEN_OBJECT_START != j_token) {\n" + " return JSON_ERROR_INVALID_TOKEN;\n" + " }\n"); + + boolean has_metainfo_enabled = FALSE; + for (i = 0; i < sdef->nElements; ++i) { + src = mputprintf(src, " boolean %s_found = FALSE;\n", sdef->elements[i].name); if (sdef->elements[i].jsonMetainfoUnbound) { - src = mputstr(src, " buf_pos = p_tok.get_buf_pos();\n"); + // initialize meta info states + src = mputprintf(src, + " int metainfo_%s = JSON_METAINFO_NONE;\n" + , sdef->elements[i].name); + has_metainfo_enabled = TRUE; } } - src = mputprintf(src, - " int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n" - " if (0 > ret_val) {\n" - " if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n" - , sdef->elements[i].name, sdef->elements[i].typedescrname); - if (sdef->elements[i].jsonMetainfoUnbound) { - src = mputprintf(src, - // undo the last action on the buffer, check if the invalid token was a null token - " p_tok.set_buf_pos(buf_pos);\n" - " p_tok.get_next_token(&j_token, NULL, NULL);\n" - " if (JSON_TOKEN_LITERAL_NULL == j_token) {\n" - " if (JSON_METAINFO_NONE == metainfo_%s) {\n" - // delay reporting an error for now, there might be meta info later - " metainfo_%s = JSON_METAINFO_NEEDED;\n" - " continue;\n" - " }\n" - " else if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n" - // meta info already found - " continue;\n" - " }\n" - " }\n" - , sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name); - } - src = mputprintf(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n" - " }\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " dec_len += (size_t)ret_val;\n" - , (unsigned long) strlen(sdef->elements[i].dispname), sdef->elements[i].dispname); + src = mputstr(src, + // Read name - value token pairs until we reach some other token + "\n while (TRUE) {\n" + " char* fld_name = 0;\n" + " size_t name_len = 0;\n" + " size_t buf_pos = p_tok.get_buf_pos();\n" + " dec_len += p_tok.get_next_token(&j_token, &fld_name, &name_len);\n" + " if (JSON_TOKEN_ERROR == j_token) {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_NAME_TOKEN_ERROR);\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + // undo the last action on the buffer + " else if (JSON_TOKEN_NAME != j_token) {\n" + " p_tok.set_buf_pos(buf_pos);\n" + " break;\n" + " }\n" + " else {\n "); if (has_metainfo_enabled) { - src = mputstr(src, " }\n"); + // check for meta info + src = mputstr(src, + "boolean is_metainfo = FALSE;\n" + " if (name_len > 9 && 0 == strncmp(fld_name, \"metainfo \", 9)) {\n" + " fld_name += 9;\n" + " name_len -= 9;\n" + " is_metainfo = TRUE;\n" + " }\n "); } - src = mputstr(src, - " }\n" - " else "); - } - src = mputprintf(src, - "{\n" - // invalid field name - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, %sJSON_DEC_INVALID_NAME_ERROR, (int)name_len, fld_name);\n" - // if this is set to a warning, skip the value of the field - " dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n" - " if (JSON_TOKEN_NUMBER != j_token && JSON_TOKEN_STRING != j_token &&\n" - " JSON_TOKEN_LITERAL_TRUE != j_token && JSON_TOKEN_LITERAL_FALSE != j_token &&\n" - " JSON_TOKEN_LITERAL_NULL != j_token) {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, (int)name_len, fld_name);\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " }\n" - " }\n" - " }\n\n" - " dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n" - " if (JSON_TOKEN_OBJECT_END != j_token) {\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_OBJECT_END_TOKEN_ERROR, \"\");\n" - " return JSON_ERROR_FATAL;\n" - " }\n\n " - , has_metainfo_enabled ? "is_metainfo ?\n JSON_DEC_METAINFO_NAME_ERROR : " : ""); - // Check if every field has been set and handle meta info - for (i = 0; i < sdef->nElements; ++i) { - if (sdef->elements[i].jsonMetainfoUnbound) { + for (i = 0; i < sdef->nElements; ++i) { src = mputprintf(src, - "if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n" - " field_%s.clean_up();\n" - " }\n" - " else if (JSON_METAINFO_NEEDED == metainfo_%s) {\n" - // no meta info was found for this field, report the delayed error - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n" - " }\n" - " else " - , sdef->elements[i].name, sdef->elements[i].name - , sdef->elements[i].name - , (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) { + // 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); + if (has_metainfo_enabled) { + src = mputstr(src, " if (is_metainfo) {\n"); + if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, + // check meta info + " char* info_value = 0;\n" + " size_t info_len = 0;\n" + " dec_len += p_tok.get_next_token(&j_token, &info_value, &info_len);\n" + " if (JSON_TOKEN_STRING == j_token && 9 == info_len &&\n" + " 0 == strncmp(info_value, \"\\\"unbound\\\"\", 9)) {\n" + " metainfo_%s = JSON_METAINFO_UNBOUND;\n" + " }\n" + " else {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_VALUE_ERROR, \"%s\");\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + , sdef->elements[i].name, sdef->elements[i].dispname); + } + else { + src = mputprintf(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_NOT_APPLICABLE, \"%s\");\n" + " return JSON_ERROR_FATAL;\n" + , sdef->elements[i].dispname); + } + src = mputstr(src, + " }\n" + " else {\n"); + if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputstr(src, " buf_pos = p_tok.get_buf_pos();\n"); + } + } src = mputprintf(src, - " field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n" + " int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n" + " if (0 > ret_val) {\n" + " if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n" , sdef->elements[i].name, sdef->elements[i].typedescrname); + if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, + // undo the last action on the buffer, check if the invalid token was a null token + " p_tok.set_buf_pos(buf_pos);\n" + " p_tok.get_next_token(&j_token, NULL, NULL);\n" + " if (JSON_TOKEN_LITERAL_NULL == j_token) {\n" + " if (JSON_METAINFO_NONE == metainfo_%s) {\n" + // delay reporting an error for now, there might be meta info later + " metainfo_%s = JSON_METAINFO_NEEDED;\n" + " continue;\n" + " }\n" + " else if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n" + // meta info already found + " continue;\n" + " }\n" + " }\n" + , sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name); + } + src = mputprintf(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n" + " }\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " dec_len += (size_t)ret_val;\n" + , (unsigned long) strlen(sdef->elements[i].dispname), sdef->elements[i].dispname); + if (has_metainfo_enabled) { + src = mputstr(src, " }\n"); + } + src = mputstr(src, + " }\n" + " else "); } - else if (sdef->elements[i].isOptional) { + src = mputprintf(src, + "{\n" + // invalid field name + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, %sJSON_DEC_INVALID_NAME_ERROR, (int)name_len, fld_name);\n" + // if this is set to a warning, skip the value of the field + " dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n" + " if (JSON_TOKEN_NUMBER != j_token && JSON_TOKEN_STRING != j_token &&\n" + " JSON_TOKEN_LITERAL_TRUE != j_token && JSON_TOKEN_LITERAL_FALSE != j_token &&\n" + " JSON_TOKEN_LITERAL_NULL != j_token) {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, (int)name_len, fld_name);\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " }\n" + " }\n" + " }\n\n" + " dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n" + " if (JSON_TOKEN_OBJECT_END != j_token) {\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_OBJECT_END_TOKEN_ERROR, \"\");\n" + " return JSON_ERROR_FATAL;\n" + " }\n\n " + , has_metainfo_enabled ? "is_metainfo ?\n JSON_DEC_METAINFO_NAME_ERROR : " : ""); + // Check if every field has been set and handle meta info + for (i = 0; i < sdef->nElements; ++i) { + if (sdef->elements[i].jsonMetainfoUnbound) { + src = mputprintf(src, + "if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n" + " field_%s.clean_up();\n" + " }\n" + " else if (JSON_METAINFO_NEEDED == metainfo_%s) {\n" + // no meta info was found for this field, report the delayed error + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n" + " }\n" + " else " + , sdef->elements[i].name, sdef->elements[i].name + , sdef->elements[i].name + , (unsigned long) strlen(sdef->elements[i].dispname) + , sdef->elements[i].dispname); + } src = mputprintf(src, - " field_%s = OMIT_VALUE;\n" + " if (!%s_found) {\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); + 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) { + 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"); + "\n return (int)dec_len;\n"); } - src = mputstr(src, - "\n return (int)dec_len;\n" - "}\n\n"); + src = mputstr(src, "}\n\n"); } /* end of class definition */ diff --git a/compiler2/union.c b/compiler2/union.c index 3223e47d0600ec2dfe79371486c63a0062cb88ac..21fe71e1b02ce62da60004171059b3953d807359 100644 --- a/compiler2/union.c +++ b/compiler2/union.c @@ -2009,21 +2009,29 @@ void defUnionClass(struct_def const *sdef, output_struct *output) // JSON encode src = mputprintf(src, "int %s::JSON_encode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok) const\n" - "{\n", name, use_runtime_2 ? " p_td" : ""); + "{\n", name, (use_runtime_2 || !sdef->jsonAsValue) ? " p_td" : ""); if (use_runtime_2) { src = mputstr(src, " if (err_descr) return JSON_encode_negtest(err_descr, p_td, p_tok);\n"); } if (!sdef->jsonAsValue) { - src = mputstr(src, " int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n\n"); + // 'as value' is not set for the base type, but it might still be set in + // the type descriptor + src = mputstr(src, + " boolean as_value = NULL != p_td.json && p_td.json->as_value;\n" + " int enc_len = as_value ? 0 : " + "p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n"); } else { - src = mputstr(src, " int enc_len = 0;\n\n"); + src = mputstr(src, " int enc_len = 0;\n"); } src = mputstr(src, " switch(union_selection) {\n"); for (i = 0; i < sdef->nElements; ++i) { src = mputprintf(src, " case %s_%s:\n", selection_prefix, sdef->elements[i].name); if (!sdef->jsonAsValue) { - src = mputprintf(src, " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + src = mputprintf(src, + " if (!as_value) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + " }\n" , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); } src = mputprintf(src, @@ -2039,7 +2047,10 @@ void defUnionClass(struct_def const *sdef, output_struct *output) " }\n\n" , dispname); if (!sdef->jsonAsValue) { - src = mputstr(src, " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n"); + src = mputstr(src, + " if (!as_value) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n" + " }\n"); } src = mputstr(src, " return enc_len;\n" @@ -2052,10 +2063,15 @@ void defUnionClass(struct_def const *sdef, output_struct *output) "const TTCN_Typedescriptor_t&, JSON_Tokenizer&) const;\n"); src = mputprintf(src, "int %s::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, " - "const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) const\n" - "{\n", name); + "const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok) const\n" + "{\n", name, !sdef->jsonAsValue ? " p_td" : ""); if (!sdef->jsonAsValue) { - src = mputstr(src, " int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n\n"); + // 'as value' is not set for the base type, but it might still be set in + // the type descriptor + src = mputstr(src, + " boolean as_value = NULL != p_td.json && p_td.json->as_value;\n" + " int enc_len = as_value ? 0 : " + "p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);\n"); } else { src = mputstr(src, " int enc_len = 0;\n\n"); } @@ -2079,7 +2095,10 @@ void defUnionClass(struct_def const *sdef, output_struct *output) " }\n" , selection_prefix, sdef->elements[i].name, (int)i, (int)i); if (!sdef->jsonAsValue) { - src = mputprintf(src, " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + src = mputprintf(src, + " if (!as_value) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + " }\n" , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); } src = mputstr(src, @@ -2088,7 +2107,10 @@ void defUnionClass(struct_def const *sdef, output_struct *output) " }\n" " } else {\n"); if (!sdef->jsonAsValue) { - src = mputprintf(src, " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + src = mputprintf(src, + " if (!as_value) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, \"%s\");\n" + " }\n" , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname); } src = mputprintf(src, @@ -2110,7 +2132,10 @@ void defUnionClass(struct_def const *sdef, output_struct *output) " }\n\n" , dispname); if (!sdef->jsonAsValue) { - src = mputstr(src, " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n"); + src = mputstr(src, + " if (!as_value) {\n" + " enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);\n" + "}\n"); } src = mputstr(src, " return enc_len;\n" @@ -2119,133 +2144,137 @@ void defUnionClass(struct_def const *sdef, output_struct *output) // JSON decode src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent)\n" + "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n" "{\n" " json_token_t j_token = JSON_TOKEN_NONE;\n" - , name); - if (sdef->jsonAsValue) { + , name, !sdef->jsonAsValue ? " p_td" : ""); + if (!sdef->jsonAsValue) { src = mputstr(src, - " size_t buf_pos = p_tok.get_buf_pos();\n" - " p_tok.get_next_token(&j_token, NULL, NULL);\n" - " int ret_val = 0;\n" - " switch(j_token) {\n" - " case JSON_TOKEN_NUMBER: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_NUMBER & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + " if (NULL != p_td.json && p_td.json->as_value) {\n"); + } + src = mputstr(src, + " size_t buf_pos = p_tok.get_buf_pos();\n" + " p_tok.get_next_token(&j_token, NULL, NULL);\n" + " int ret_val = 0;\n" + " switch(j_token) {\n" + " case JSON_TOKEN_NUMBER: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_NUMBER & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"number\");\n" - " clean_up();\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " case JSON_TOKEN_STRING: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_STRING & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + } + src = mputstr(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"number\");\n" + " clean_up();\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " case JSON_TOKEN_STRING: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_STRING & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"string\");\n" - " clean_up();\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " case JSON_TOKEN_LITERAL_TRUE:\n" - " case JSON_TOKEN_LITERAL_FALSE: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_BOOLEAN & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + } + src = mputstr(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"string\");\n" + " clean_up();\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " case JSON_TOKEN_LITERAL_TRUE:\n" + " case JSON_TOKEN_LITERAL_FALSE: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_BOOLEAN & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " char* literal_str = mprintf(\"literal (%s)\",\n" - " (JSON_TOKEN_LITERAL_TRUE == j_token) ? \"true\" : \"false\");\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, literal_str);\n" - " Free(literal_str);\n" - " clean_up();\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " case JSON_TOKEN_ARRAY_START: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_ARRAY & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + } + src = mputstr(src, + " char* literal_str = mprintf(\"literal (%s)\",\n" + " (JSON_TOKEN_LITERAL_TRUE == j_token) ? \"true\" : \"false\");\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, literal_str);\n" + " Free(literal_str);\n" + " clean_up();\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " case JSON_TOKEN_ARRAY_START: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_ARRAY & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"array\");\n" - " clean_up();\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " case JSON_TOKEN_OBJECT_START: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_OBJECT & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + } + src = mputstr(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"array\");\n" + " clean_up();\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " case JSON_TOKEN_OBJECT_START: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_OBJECT & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"object\");\n" - " clean_up();\n" - " return JSON_ERROR_FATAL;\n" - " }\n" - " case JSON_TOKEN_LITERAL_NULL: {\n"); - for (i = 0; i < sdef->nElements; ++i) { - if (JSON_NULL & sdef->elements[i].jsonValueType) { - src = mputprintf(src, - " p_tok.set_buf_pos(buf_pos);\n" - " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" - " if (0 <= ret_val) {\n" - " return ret_val;\n" - " }\n" - , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); - } + } + src = mputstr(src, + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_AS_VALUE_ERROR, \"object\");\n" + " clean_up();\n" + " return JSON_ERROR_FATAL;\n" + " }\n" + " case JSON_TOKEN_LITERAL_NULL: {\n"); + for (i = 0; i < sdef->nElements; ++i) { + if (JSON_NULL & sdef->elements[i].jsonValueType) { + src = mputprintf(src, + " p_tok.set_buf_pos(buf_pos);\n" + " ret_val = %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n" + " if (0 <= ret_val) {\n" + " return ret_val;\n" + " }\n" + , at_field, sdef->elements[i].name, sdef->elements[i].typedescrname); } - src = mputstr(src, - " clean_up();\n" - // the caller might be able to decode the null value if it's an optional field - // only return an invalid token error, not a fatal error - " return JSON_ERROR_INVALID_TOKEN;\n" - " }\n" - " case JSON_TOKEN_ERROR:\n" - " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n" - " return JSON_ERROR_FATAL;\n" - " default:\n" - " return JSON_ERROR_INVALID_TOKEN;\n" - " }\n" - " return 0;\n" - "}\n\n"); - } else { // not "as value" + } + src = mputstr(src, + " clean_up();\n" + // the caller might be able to decode the null value if it's an optional field + // only return an invalid token error, not a fatal error + " return JSON_ERROR_INVALID_TOKEN;\n" + " }\n" + " case JSON_TOKEN_ERROR:\n" + " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n" + " return JSON_ERROR_FATAL;\n" + " default:\n" + " return JSON_ERROR_INVALID_TOKEN;\n" + " }\n" + " return ret_val;\n"); + if (!sdef->jsonAsValue) { src = mputprintf(src, + " }\n" + " else {\n" // if there is no 'as value' set in the type descriptor " size_t dec_len = p_tok.get_next_token(&j_token, NULL, NULL);\n" " if (JSON_TOKEN_ERROR == j_token) {\n" " JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n" @@ -2292,8 +2321,9 @@ void defUnionClass(struct_def const *sdef, output_struct *output) " return JSON_ERROR_FATAL;\n" " }\n\n" " return (int)dec_len;\n" - "}\n\n"); + " }\n"); } + src = mputstr(src, "}\n\n"); } /* end of class definition */ diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc index 0d130b10354886230eff957f76c35db950560c75..3333a69af800fb184d7c116e7867e0ff9be61218 100644 --- a/core2/Basetype2.cc +++ b/core2/Basetype2.cc @@ -5758,6 +5758,12 @@ int Record_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& return -1; } + if (NULL != p_td.json && p_td.json->as_value) { + // if 'as value' is set, then the record/set has only one field, + // encode that without any brackets or field names + return get_at(0)->JSON_encode(*fld_descr(0), p_tok); + } + int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL); int field_count = get_count(); @@ -5786,7 +5792,7 @@ int Record_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& } int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, - const TTCN_Typedescriptor_t& /*p_td*/, + const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok) const { if (!is_bound()) { @@ -5795,7 +5801,9 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, return -1; } - int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL); + boolean as_value = NULL != p_td.json && p_td.json->as_value; + + int enc_len = as_value ? 0 : p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL); int values_idx = 0; int edescr_idx = 0; @@ -5809,7 +5817,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, const Erroneous_values_t* err_vals = p_err_descr->next_field_err_values(i, values_idx); const Erroneous_descriptor_t* emb_descr = p_err_descr->next_field_emb_descr(i, edescr_idx); - if (NULL != err_vals && NULL != err_vals->before) { + if (!as_value && NULL != err_vals && NULL != err_vals->before) { if (NULL == err_vals->before->errval) { TTCN_error("internal error: erroneous before value missing"); } @@ -5836,16 +5844,20 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, TTCN_error("internal error: erroneous before typedescriptor missing"); } // only replace the field's value, keep the field name - enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name); + if (!as_value) { + enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name); + } enc_len += err_vals->value->errval->JSON_encode(*(err_vals->value->type_descr), p_tok); } } } else { boolean metainfo_unbound = NULL != fld_descr(i)->json && fld_descr(i)->json->metainfo_unbound; if ((NULL != fld_descr(i)->json && fld_descr(i)->json->omit_as_null) || - get_at(i)->is_present() || metainfo_unbound) { - enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name); - if (metainfo_unbound && !get_at(i)->is_bound()) { + get_at(i)->is_present() || metainfo_unbound || as_value) { + if (!as_value) { + enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name); + } + if (!as_value && metainfo_unbound && !get_at(i)->is_bound()) { enc_len += p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL); char* metainfo_str = mprintf("metainfo %s", field_name); enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, metainfo_str); @@ -5860,7 +5872,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, } } - if (NULL != err_vals && NULL != err_vals->after) { + if (!as_value && NULL != err_vals && NULL != err_vals->after) { if (NULL == err_vals->after->errval) { TTCN_error("internal error: erroneous after value missing"); } @@ -5881,12 +5893,19 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, } } - enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL); + if (!as_value) { + enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL); + } return enc_len; } -int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& /*p_td*/, JSON_Tokenizer& p_tok, boolean p_silent) +int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent) { + if (NULL != p_td.json && p_td.json->as_value) { + // if 'as value' is set, then the record/set has only one field, + // decode that without the need of any brackets or field names + return get_at(0)->JSON_decode(*fld_descr(0), p_tok, p_silent); + } json_token_t token = JSON_TOKEN_NONE; size_t dec_len = p_tok.get_next_token(&token, NULL, NULL); if (JSON_TOKEN_ERROR == token) { diff --git a/regression_test/json/AttributeTestcases.ttcn b/regression_test/json/AttributeTestcases.ttcn index 247a1e9fa8c278b4d486ef914d6751f46693ec63..b82b0c1ada8bb8af24f5b129b55053338c0dd8ec 100644 --- a/regression_test/json/AttributeTestcases.ttcn +++ b/regression_test/json/AttributeTestcases.ttcn @@ -352,6 +352,91 @@ testcase tc_attribute_metainfo_for_unbound_arrays() runs on MTC { f_bool2verdict( log2str(f_dec_meta_arr(os)) == log2str(arr) ); } +testcase tc_attribute_as_value_rec() runs on MTC { + var RecAsValue val := { field := 10 }; + var bitstring enc_exp := oct2bit(char2oct("10")); + var bitstring enc := encvalue(val); + if (enc != enc_exp) { + setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc); + } + else { + var RecAsValue dec; + var integer res := decvalue(enc, dec); + if (res != 0) { + setverdict(fail, "Decoding failed. Result: ", res); + } + else if (lengthof(enc) != 0) { + setverdict(fail, "Decoding failed. Remaining bits: ", enc); + } + else if (dec != val) { + setverdict(fail, "Decoding failed. Expected value: ", val, ", got: ", dec); + } + else { + setverdict(pass); + } + } +} + +testcase tc_attribute_not_as_value_rec() runs on MTC { + var RecNotAsValue val := { field := { 1.0, 2.0 } }; + var bitstring enc_exp := oct2bit(char2oct("{\"field\":[1.000000,2.000000]}")); + var bitstring enc := encvalue(val); + if (enc != enc_exp) { + setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc); + } + else { + var RecNotAsValue dec; + var integer res := decvalue(enc, dec); + if (res != 0) { + setverdict(fail, "Decoding failed. Result: ", res); + } + else if (lengthof(enc) != 0) { + setverdict(fail, "Decoding failed. Remaining bits: ", enc); + } + else if (dec != val) { + setverdict(fail, "Decoding failed. Expected value: ", val, ", got: ", dec); + } + else { + setverdict(pass); + } + } +} + +testcase tc_attribute_as_value_fields() runs on MTC { + var SetWithAsValueFields val := { + uni := { i := -3 }, + rec := { field := { 1.0, 2.0 } }, + list := { + { integer := 12 }, + { charstring := "abc" }, + { R := { i := 6, cs := "xy", b := false, d := Wednesday } }, + { Thing := { bval := true } }, + { RoI := { 1, 2, 3 } } + } + }; + var bitstring enc_exp := oct2bit(char2oct("{\"uni\":-3,\"rec\":[1.000000,2.000000],\"list\":[12,\"abc\",{\"i\":6,\"cs\":\"xy\",\"b\":false,\"d\":\"Wednesday\"},true,[1,2,3]]}")); + var bitstring enc := encvalue(val); + if (enc != enc_exp) { + setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc); + } + else { + var SetWithAsValueFields dec; + var integer res := decvalue(enc, dec); + if (res != 0) { + setverdict(fail, "Decoding failed. Result: ", res); + } + else if (lengthof(enc) != 0) { + setverdict(fail, "Decoding failed. Remaining bits: ", enc); + } + else if (dec != val) { + setverdict(fail, "Decoding failed. Expected value: ", val, ", got: ", dec); + } + else { + setverdict(pass); + } + } +} + control { execute(tc_NoAttributeOnUpperLevel()) @@ -372,5 +457,8 @@ control { execute(tc_attribute_metainfo_for_unbound_negtest()); execute(tc_attribute_metainfo_for_unbound_empty_rec()); execute(tc_attribute_metainfo_for_unbound_arrays()); + execute(tc_attribute_as_value_rec()); + execute(tc_attribute_not_as_value_rec()); + execute(tc_attribute_as_value_fields()); } } diff --git a/regression_test/json/Types.ttcn b/regression_test/json/Types.ttcn index d2953efc8a78aef9f8050671eda2ef066c2922bd..883317f52f3e51035c321ffad50807d5d9bb4070 100644 --- a/regression_test/json/Types.ttcn +++ b/regression_test/json/Types.ttcn @@ -264,6 +264,34 @@ with { variant "JSON: metainfo for unbound"; } type float MetainfoArray[3] with { variant "JSON: metainfo for unbound"; } +type record RecAsValue { + integer field +} +with { + variant "JSON: as value"; +} + +type record RecNotAsValue { + RoF field +} + +type union UniNotAsValue { + integer i, + octetstring o +} + +type set SetWithAsValueFields { + UniNotAsValue uni, + RecNotAsValue rec, + record of anytype list +} +with { + variant (uni) "JSON: as value"; + variant (rec) "JSON: as value"; + variant (list[-]) "JSON: as value"; +} + } with { encode "JSON"; + extension "anytype integer, charstring, R, RoI, Thing"; } diff --git a/usrguide/referenceguide.doc b/usrguide/referenceguide.doc index 3a9e5f1d399eb5adb8092cd43159fb570e210839..9ada0d4c31829ad79b26f6616abd3dbeaa0654c0 100644 Binary files a/usrguide/referenceguide.doc and b/usrguide/referenceguide.doc differ