Commit 468b295f authored by Botond Baranyi's avatar Botond Baranyi
Browse files

Implemented JSON attribute 'as map' (bug 540051)



Change-Id: I5d228318532b9e6774e77c7604e63e2fba4afab5
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 284c5813
......@@ -3013,6 +3013,34 @@ namespace Common {
if (NULL != jsonattrib->tag_list) {
chk_json_tag_list();
}
if (jsonattrib->as_map) {
Type* last = get_type_refd_last();
if (T_SEQOF != last->typetype && T_SETOF != last->typetype) {
error("Invalid attribute, 'as map' requires record of or set of");
}
else {
Type* of_type = last->get_ofType();
Type* of_type_last = of_type->get_type_refd_last();
if ((T_SEQ_T != of_type_last->typetype &&
T_SET_T != of_type_last->typetype) ||
of_type_last->get_nof_comps() != 2) {
error("Invalid attribute, 'as map' requires the element type to be "
"a record or set with 2 fields");
}
else {
Type* key_type = of_type_last->get_comp_byIndex(0)->get_type();
if (key_type->get_type_refd_last()->get_typetype() != T_USTR) {
error("Invalid attribute, 'as map' requires the element type's "
"first field to be a universal charstring");
}
if (key_type->is_optional_field()) {
error("Invalid attribute, 'as map' requires the element type's "
"first field to be mandatory");
}
}
}
}
}
}
......
......@@ -1091,23 +1091,30 @@ void Type::generate_code_jsondescriptor(output_struct *target)
target->header.global_vars = mputprintf(target->header.global_vars,
"extern const TTCN_JSONdescriptor_t %s_json_;\n", get_genname_own().c_str());
boolean as_map = (jsonattrib != NULL && jsonattrib->as_map) ||
(ownertype == OT_RECORD_OF && parent_type->jsonattrib != NULL &&
parent_type->jsonattrib->as_map);
if (NULL == jsonattrib) {
target->source.global_vars = mputprintf(target->source.global_vars,
"const TTCN_JSONdescriptor_t %s_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };\n"
, get_genname_own().c_str());
"const TTCN_JSONdescriptor_t %s_json_ = { FALSE, NULL, FALSE, NULL, "
"FALSE, FALSE, %s };\n"
, get_genname_own().c_str(), as_map ? "TRUE" : "FALSE");
} else {
char* alias = jsonattrib->alias ? mputprintf(NULL, "\"%s\"", jsonattrib->alias) : NULL;
char* def_val = jsonattrib->default_value ?
mputprintf(NULL, "\"%s\"", jsonattrib->default_value) : NULL;
target->source.global_vars = mputprintf(target->source.global_vars,
"const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s };\n"
"const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s, %s };\n"
, get_genname_own().c_str()
, jsonattrib->omit_as_null ? "TRUE" : "FALSE"
, alias ? alias : "NULL"
, (jsonattrib->as_value || jsonattrib->tag_list != NULL) ? "TRUE" : "FALSE"
, def_val ? def_val : "NULL"
, jsonattrib->metainfo_unbound ? "TRUE" : "FALSE"
, jsonattrib->as_number ? "TRUE" : "FALSE");
, jsonattrib->as_number ? "TRUE" : "FALSE"
, as_map ? "TRUE" : "FALSE");
Free(alias);
Free(def_val);
}
......@@ -1737,6 +1744,18 @@ void Type::generate_code_Se(output_struct *target)
FATAL_ERROR("Type::generate_code_Se()"); // union only, not for record
}
}
if (jsonattrib != NULL) {
sdef.jsonAsValue = jsonattrib->as_value;
}
if (sdef.nElements == 2) {
Type* first_field_type = get_comp_byIndex(0)->get_type();
sdef.jsonAsMapPossible = !first_field_type->is_optional_field() &&
first_field_type->get_type_refd_last()->typetype == T_USTR;
}
else {
sdef.jsonAsMapPossible = FALSE;
}
sdef.elements = (struct_field*)
Malloc(sdef.totalElements*sizeof(*sdef.elements));
memset(sdef.elements, 0, sdef.totalElements * sizeof(*sdef.elements));
......
......@@ -121,6 +121,7 @@ typedef struct {
boolean xerHasNamespaces; /* from the module */
boolean xerEmbedValuesPossible; /* for sequence */
boolean jsonAsValue; /* for both */
boolean jsonAsMapPossible; /* for sequence */
/** The index of the last field which can generate empty XML, or -1 */
int exerMaybeEmptyIndex; /* for union */
const char * control_ns_prefix;
......
......@@ -3226,7 +3226,8 @@ char* generate_json_decoder(char* src, const struct_def* sdef)
src = mputprintf(src,
"int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, "
"boolean p_silent, int)\n"
"{\n", sdef->name, (sdef->nElements == 1 && !sdef->jsonAsValue) ? " p_td" : "");
"{\n", sdef->name,
((sdef->nElements == 1 && !sdef->jsonAsValue) || sdef->jsonAsMapPossible) ? " p_td" : "");
if (sdef->nElements == 1) {
if (!sdef->jsonAsValue) {
......@@ -3238,9 +3239,31 @@ char* generate_json_decoder(char* src, const struct_def* sdef)
src = mputstr(src, " }\n");
}
}
if (!sdef->jsonAsValue) {
src = mputstr(src, " json_token_t j_token = JSON_TOKEN_NONE;\n");
}
if (sdef->jsonAsMapPossible) {
src = mputprintf(src,
" if (p_td.json->as_map) {\n"
" char* fld_name = NULL;\n"
" size_t name_len = 0;\n"
" size_t buf_pos = p_tok.get_buf_pos();\n"
" size_t 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_BAD_TOKEN_ERROR, \"\");\n"
" return JSON_ERROR_FATAL;\n"
" }\n"
" else if (JSON_TOKEN_NAME != j_token) {\n"
" p_tok.set_buf_pos(buf_pos);\n"
" return JSON_ERROR_INVALID_TOKEN;\n"
" }\n"
" field_%s.decode_utf8(name_len, (unsigned char*) fld_name);\n"
" return field_%s.JSON_decode(%s_descr_, p_tok, p_silent) + dec_len;\n"
" }\n", sdef->elements[0].name,
sdef->elements[1].name, sdef->elements[1].typedescrname);
}
if (!sdef->jsonAsValue) {
src = mputstr(src,
" 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"
......@@ -4738,7 +4761,8 @@ void defRecordClass1(const struct_def *sdef, output_struct *output)
" TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,\n"
" \"Encoding an unbound value of type %s.\");\n"
" return -1;\n"
" }\n\n", name, (sdef->nElements == 1 && !sdef->jsonAsValue) ? " p_td" : "", dispname);
" }\n\n", name,
((sdef->nElements == 1 && !sdef->jsonAsValue) || sdef->jsonAsMapPossible) ? " p_td" : "", dispname);
if (sdef->nElements == 1) {
if (!sdef->jsonAsValue) {
src = mputstr(src, " if (NULL != p_td.json && p_td.json->as_value) {\n");
......@@ -4749,6 +4773,18 @@ void defRecordClass1(const struct_def *sdef, output_struct *output)
src = mputstr(src, " }\n");
}
}
if (sdef->jsonAsMapPossible) {
src = mputprintf(src,
" if (p_td.json->as_map) {\n"
" TTCN_Buffer key_buf;\n"
" field_%s.encode_utf8(key_buf);\n"
" CHARSTRING key_str;\n"
" key_buf.get_string(key_str);\n"
" return p_tok.put_next_token(JSON_TOKEN_NAME, (const char*) key_str) + \n"
" field_%s.JSON_encode(%s_descr_, p_tok);\n"
" }\n", sdef->elements[0].name,
sdef->elements[1].name, sdef->elements[1].typedescrname);
}
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) {
......
......@@ -1545,7 +1545,8 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output)
" \"Encoding an unbound value of type %s.\");\n"
" return -1;\n"
" }\n\n"
" int enc_len = p_tok.put_next_token(JSON_TOKEN_ARRAY_START, NULL);\n"
" int enc_len = p_tok.put_next_token(p_td.json->as_map ? "
"JSON_TOKEN_OBJECT_START : JSON_TOKEN_ARRAY_START, NULL);\n"
" for (int i = 0; i < val_ptr->n_elements; ++i) {\n"
" if (NULL != p_td.json && p_td.json->metainfo_unbound && !(*this)[i].is_bound()) {\n"
// unbound elements are encoded as { "metainfo []" : "unbound" }
......@@ -1560,7 +1561,8 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output)
" enc_len += ret_val;\n"
" }\n"
" }\n"
" enc_len += p_tok.put_next_token(JSON_TOKEN_ARRAY_END, NULL);\n"
" enc_len += p_tok.put_next_token(p_td.json->as_map ? "
"JSON_TOKEN_OBJECT_END : JSON_TOKEN_ARRAY_END, NULL);\n"
" return enc_len;\n"
"}\n\n"
, name, dispname);
......@@ -1581,7 +1583,8 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output)
" JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, \"\");\n"
" return JSON_ERROR_FATAL;\n"
" }\n"
" else if (JSON_TOKEN_ARRAY_START != token) {\n"
" else if ((!p_td.json->as_map && JSON_TOKEN_ARRAY_START != token) ||\n"
" (p_td.json->as_map && JSON_TOKEN_OBJECT_START != token)) {\n"
" return JSON_ERROR_INVALID_TOKEN;\n"
" }\n\n"
" set_size(0);\n"
......@@ -1632,7 +1635,8 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output)
" dec_len += (size_t)ret_val2;\n"
" }\n\n"
" dec_len += p_tok.get_next_token(&token, NULL, NULL);\n"
" if (JSON_TOKEN_ARRAY_END != token) {\n"
" if ((!p_td.json->as_map && JSON_TOKEN_ARRAY_END != token) ||\n"
" (p_td.json->as_map && JSON_TOKEN_OBJECT_END != token)) {\n"
" JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_REC_OF_END_TOKEN_ERROR, \"\");\n"
" if (p_silent) {\n"
" clean_up();\n"
......
......@@ -36,6 +36,7 @@ void JsonAST::init_JsonAST()
metainfo_unbound = false;
as_number = false;
tag_list = NULL;
as_map = false;
}
JsonAST::JsonAST(const JsonAST *other_val)
......@@ -51,6 +52,7 @@ JsonAST::JsonAST(const JsonAST *other_val)
schema_extensions.add(new JsonSchemaExtension(*other_val->schema_extensions[i]));
}
metainfo_unbound = other_val->metainfo_unbound;
as_map = other_val->as_map;
}
}
......@@ -119,4 +121,7 @@ void JsonAST::print_JsonAST() const
}
}
}
if (as_map) {
printf("Encoding elements into a map of key-value pairs.\n\r");
}
}
......@@ -43,6 +43,7 @@ class JsonAST {
boolean metainfo_unbound;
boolean as_number;
rawAST_tag_list* tag_list;
boolean as_map;
JsonAST() { init_JsonAST(); }
JsonAST(const JsonAST *other_val);
......
......@@ -500,6 +500,7 @@ for RETURN(XKWfor);
unbound RETURN(XKWunbound);
chosen RETURN(XChosenKeyword);
otherwise RETURN(XJsonOtherwise);
map RETURN(XJsonMap);
}
<INITIAL>{
......
......@@ -298,6 +298,7 @@ static void yyprint(FILE *file, int type, const YYSTYPE& value);
%token XAsValueKeyword "asValue"
%token XChosenKeyword "chosen"
%token XJsonOtherwise "otherwise"
%token XJsonMap "map"
%type <enumval>
......@@ -1840,6 +1841,7 @@ JSONattribute:
| JMetainfoForUnbound
| JAsNumber
| JChosen
| JAsMap
;
JOmitAsNull:
......@@ -1880,6 +1882,10 @@ JChosen:
}
;
JAsMap:
XKWas XJsonMap { jsonstruct->as_map = true; }
;
%%
/* parse_rawAST(), which calls our rawAST_parse, is over in rawASST.l */
......
......@@ -32,55 +32,55 @@
#include <sys/types.h>
// JSON descriptors for base types
const TTCN_JSONdescriptor_t INTEGER_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t INTEGER_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE };
const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
......
......@@ -66,6 +66,13 @@ struct TTCN_JSONdescriptor_t
* number, instead of its name form as a JSON string (affects both encoding
* and decoding). */
boolean as_number;
/** If set, encodes the value into a map of key-value pairs (i.e. a fully
* customizable JSON object). The encoded type has to be a record of/set of
* with a record/set element type, which has 2 fields, the first of which is
* a non-optional universal charstring.
* Example: { "key1" : value1, "key2" : value2 } */
boolean as_map;
};
/** This macro makes sure that coding errors will only be displayed if the silent
......
......@@ -1476,7 +1476,8 @@ int Record_Of_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenize
return -1;
}
int enc_len = p_tok.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
int enc_len = p_tok.put_next_token(p_td.json->as_map ? JSON_TOKEN_OBJECT_START :
JSON_TOKEN_ARRAY_START, NULL);
for (int i = 0; i < get_nof_elements(); ++i) {
if (NULL != p_td.json && p_td.json->metainfo_unbound && !get_at(i)->is_bound()) {
......@@ -1493,7 +1494,8 @@ int Record_Of_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenize
}
}
enc_len += p_tok.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
enc_len += p_tok.put_next_token(p_td.json->as_map ? JSON_TOKEN_OBJECT_END :
JSON_TOKEN_ARRAY_END, NULL);
return enc_len;
}
......@@ -1507,7 +1509,8 @@ int Record_Of_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_desc
return -1;
}
int enc_len = p_tok.put_next_token(JSON_TOKEN_ARRAY_START, NULL);
int enc_len = p_tok.put_next_token(p_td.json->as_map ? JSON_TOKEN_OBJECT_START :
JSON_TOKEN_ARRAY_START, NULL);
int values_idx = 0;
int edescr_idx = 0;
......@@ -1583,7 +1586,8 @@ int Record_Of_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_desc
}
}
enc_len += p_tok.put_next_token(JSON_TOKEN_ARRAY_END, NULL);
enc_len += p_tok.put_next_token(p_td.json->as_map ? JSON_TOKEN_OBJECT_END :
JSON_TOKEN_ARRAY_END, NULL);
return enc_len;
}
......@@ -1601,7 +1605,8 @@ int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenize
JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, "");
return JSON_ERROR_FATAL;
}
else if (JSON_TOKEN_ARRAY_START != token) {
else if ((!p_td.json->as_map && JSON_TOKEN_ARRAY_START != token) ||
(p_td.json->as_map && JSON_TOKEN_OBJECT_START != token)) {
return JSON_ERROR_INVALID_TOKEN;
}
......@@ -1661,7 +1666,8 @@ int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenize
}
dec_len += p_tok.get_next_token(&token, NULL, NULL);
if (JSON_TOKEN_ARRAY_END != token) {
if ((!p_td.json->as_map && JSON_TOKEN_ARRAY_END != token) ||
(p_td.json->as_map && JSON_TOKEN_OBJECT_END != token)) {
JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_REC_OF_END_TOKEN_ERROR, "");
if (p_silent) {
clean_up();
......@@ -5951,6 +5957,21 @@ int Record_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&
return get_at(0)->JSON_encode(*fld_descr(0), p_tok);
}
if (p_td.json->as_map) { // TODO: implement negtest
const UNIVERSAL_CHARSTRING* key_ustr = dynamic_cast<
const UNIVERSAL_CHARSTRING*>(get_at(0));
if (NULL == key_ustr) {
TTCN_error("Internal error: attribute 'as map' is set, but the first "
"field is not a universal charstring");
}
TTCN_Buffer key_buf;
key_ustr->encode_utf8(key_buf);
CHARSTRING key_str;
key_buf.get_string(key_str);
return p_tok.put_next_token(JSON_TOKEN_NAME, (const char*) key_str) +
get_at(1)->JSON_encode(*fld_descr(1), p_tok);
}
int enc_len = p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
int field_count = get_count();
......@@ -6093,7 +6114,33 @@ int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&
// 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;
if (p_td.json->as_map) {
UNIVERSAL_CHARSTRING* key_ustr = dynamic_cast<
UNIVERSAL_CHARSTRING*>(get_at(0));
if (NULL == key_ustr) {
TTCN_error("Internal error: attribute 'as map' is set, but the first "
"field is not a universal charstring");
}
char* name = NULL;
size_t name_len = 0;
size_t buf_pos = p_tok.get_buf_pos();
size_t dec_len = p_tok.get_next_token(&token, &name, &name_len);
if (JSON_TOKEN_ERROR == token) {
JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, "");
return JSON_ERROR_FATAL;
}
else if (JSON_TOKEN_NAME != token) {
p_tok.set_buf_pos(buf_pos);
return JSON_ERROR_INVALID_TOKEN;
}
key_ustr->decode_utf8(name_len, (unsigned char*) name);
return get_at(1)->JSON_decode(*fld_descr(1), p_tok, p_silent) + dec_len;
}
size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);
if (JSON_TOKEN_ERROR == token) {
JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_BAD_TOKEN_ERROR, "");
......
......@@ -675,6 +675,20 @@ testcase tc_attribute_chosen_default() runs on MTC {
setverdict(pass);
}
testcase tc_attribute_map() runs on MTC {
var Map x := { { "one", 1 }, { "two", 2 }, { "three", 3 } };
var octetstring enc_exp := char2oct("{\"one\":1,\"two\":2,\"three\":3}");
var octetstring enc := f_enc_map(x);
if (enc != enc_exp) {
setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc);
}
var Map dec := f_dec_map(enc_exp);
if (dec != x) {
setverdict(fail, "Decoding failed. Expected: ", x, ", got: ", dec);
}
setverdict(pass);
}
control {
execute(tc_NoAttributeOnUpperLevel())
......@@ -704,5 +718,6 @@ control {
execute(tc_attribute_chosen());
execute(tc_attribute_chosen_negtest());
execute(tc_attribute_chosen_default());
execute(tc_attribute_map());
}
}
......@@ -203,6 +203,9 @@ external function f_enc_multi_rec(in MultiLevelRec x) return octetstring
external function f_enc_multi_list(in MultiLevelList x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_map(in Map x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
// for ASN.1 types
external function f_enc_seqofint(in SeqOfInt x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
......@@ -405,6 +408,9 @@ external function f_dec_multi_rec(in octetstring x) return MultiLevelRec
external function f_dec_multi_list(in octetstring x) return MultiLevelList
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_map(in octetstring x) return Map
with { extension "prototype(convert) decode(JSON)" }
// for ASN.1 types
external function f_dec_seqofint(in octetstring x) return SeqOfInt
with { extension "prototype(convert) decode(JSON)" }
......
......@@ -362,6 +362,14 @@ with {
variant (protocolId2) "default (0)";
}
type record MapItem {
universal charstring key,
integer value_
}
type set of MapItem Map
with { variant "as map" }
} with {
encode "JSON";
extension "anytype integer, charstring, R, RoI, Thing";
......
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