Commit a06ecc81 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

Implemented JSON attribute 'text ... as ...', and fixed other JSON errors (bug 540262)



Change-Id: I354765ed75a6aa20a37d065836eb71879b688cbe
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 8ecd6580
......@@ -3004,10 +3004,15 @@ namespace Common {
"record of, set of, array or field of a record or set");
}
}
if (jsonattrib->as_number &&
get_type_refd_last()->get_typetype_ttcn3() != T_ENUM_T) {
error("Invalid attribute, 'as number' is only allowed for enumerated "
"types");
if (jsonattrib->as_number) {
if (get_type_refd_last()->get_typetype_ttcn3() != T_ENUM_T) {
error("Invalid attribute, 'as number' is only allowed for enumerated "
"types");
}
else if (0 != jsonattrib->enum_texts.size()) {
warning("Attribute 'text ... as ...' will be ignored, because the "
"enumerated values are encoded as numbers");
}
}
if (NULL != jsonattrib->tag_list) {
......@@ -3041,6 +3046,35 @@ namespace Common {
}
}
}
if (0 != jsonattrib->enum_texts.size()) {
Type* last = get_type_refd_last();
if (T_ENUM_T != last->get_typetype_ttcn3()) {
error("Invalid attribute, 'text ... as ...' requires an enumerated "
"type");
}
else {
for (size_t i = 0; i < jsonattrib->enum_texts.size(); ++i) {
Identifier id(Identifier::ID_TTCN,
string(jsonattrib->enum_texts[i]->from), true);
if (!last->has_ei_withName(id)) {
error("Attribute 'text ... as ...' refers to invalid enumerated "
"value '%s'", jsonattrib->enum_texts[i]->from);
}
else {
jsonattrib->enum_texts[i]->index = static_cast<int>(
last->get_eis_index_byName(id));
for (size_t j = 0; j < i; ++j) {
if (jsonattrib->enum_texts[j]->index ==
jsonattrib->enum_texts[i]->index) {
error("Duplicate attribute 'text ... as ...' for enumerated "
"value '%s'", jsonattrib->enum_texts[i]->from);
}
}
}
}
}
}
}
}
......@@ -7631,7 +7665,11 @@ namespace Common {
{
Type *t = this;
while (true) {
if (t->has_encoding(CT_JSON)) return t->get_genname_own(my_scope);
if ((t->jsonattrib != NULL && !t->jsonattrib->empty()) ||
(t->ownertype == OT_RECORD_OF && t->parent_type->jsonattrib != NULL &&
t->parent_type->jsonattrib->as_map)) {
return t->get_genname_own(my_scope);
}
else if (t->is_ref()) t = t->get_type_refd();
else break;
}
......
......@@ -1098,15 +1098,33 @@ void Type::generate_code_jsondescriptor(output_struct *target)
if (NULL == jsonattrib) {
target->source.global_vars = mputprintf(target->source.global_vars,
"const TTCN_JSONdescriptor_t %s_json_ = { FALSE, NULL, FALSE, NULL, "
"FALSE, FALSE, %s };\n"
"FALSE, FALSE, %s, 0, NULL };\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;
char* enum_texts_name;
if (0 != jsonattrib->enum_texts.size()) {
enum_texts_name = mprintf("%s_json_enum_texts", get_genname_own().c_str());
target->source.global_vars = mputprintf(target->source.global_vars,
"const JsonEnumText %s[] = { ", enum_texts_name);
for (size_t i = 0; i < jsonattrib->enum_texts.size(); ++i) {
target->source.global_vars = mputprintf(target->source.global_vars,
"%s{ %d, \"%s\" }", i == 0 ? "" : ", ",
jsonattrib->enum_texts[i]->index, jsonattrib->enum_texts[i]->to);
}
target->source.global_vars = mputstr(target->source.global_vars,
" };\n");
}
else {
enum_texts_name = mcopystr("NULL");
}
target->source.global_vars = mputprintf(target->source.global_vars,
"const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s, %s };\n"
"const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s, %s, "
"%d, %s };\n"
, get_genname_own().c_str()
, jsonattrib->omit_as_null ? "TRUE" : "FALSE"
, alias ? alias : "NULL"
......@@ -1114,9 +1132,12 @@ void Type::generate_code_jsondescriptor(output_struct *target)
, def_val ? def_val : "NULL"
, jsonattrib->metainfo_unbound ? "TRUE" : "FALSE"
, jsonattrib->as_number ? "TRUE" : "FALSE"
, as_map ? "TRUE" : "FALSE");
, as_map ? "TRUE" : "FALSE"
, static_cast<int>(jsonattrib->enum_texts.size())
, enum_texts_name);
Free(alias);
Free(def_val);
Free(enum_texts_name);
}
}
......@@ -1418,7 +1439,7 @@ void Type::generate_code_Choice(output_struct *target)
case T_SEQOF:
case T_SETOF:
case T_ARRAY:
sdef.elements[i].jsonValueType = JSON_ARRAY;
sdef.elements[i].jsonValueType = JSON_ARRAY | JSON_OBJECT;
break;
default:
FATAL_ERROR("Type::generate_code_Choice - invalid field type %d", tt);
......
......@@ -802,8 +802,23 @@ void defEnumClass(const enum_def *edef, output_struct *output)
" \"Encoding an unbound value of enumerated type %s.\");\n"
" return -1;\n"
" }\n\n"
" char* tmp_str = p_td.json->as_number ? mprintf(\"%%d\", enum_value) : "
"mprintf(\"\\\"%%s\\\"\", enum_to_str(enum_value));\n"
" char* tmp_str;\n"
" if (p_td.json->as_number) {"
" tmp_str = mprintf(\"%%d\", enum_value);\n"
" }\n"
" else {\n"
" boolean text_found = false;\n"
" for (size_t i = 0; i < p_td.json->nof_enum_texts; ++i) {\n"
" if (p_td.json->enum_texts[i].index == enum_value) {\n"
" tmp_str = mprintf(\"\\\"%%s\\\"\", p_td.json->enum_texts[i].text);\n"
" text_found = true;\n"
" break;\n"
" }\n"
" }\n"
" if (!text_found) {\n"
" tmp_str = mprintf(\"\\\"%%s\\\"\", enum_to_str(enum_value));\n"
" }\n"
" }\n"
" int enc_len = p_tok.put_next_token(p_td.json->as_number ? "
"JSON_TOKEN_NUMBER : JSON_TOKEN_STRING, tmp_str);\n"
" Free(tmp_str);\n"
......@@ -835,7 +850,17 @@ void defEnumClass(const enum_def *edef, output_struct *output)
" else if ((JSON_TOKEN_STRING == token && !p_td.json->as_number) || use_default) {\n"
" if (use_default || (value_len > 2 && value[0] == '\\\"' && value[value_len - 1] == '\\\"')) {\n"
" if (!use_default) value[value_len - 1] = 0;\n"
" enum_value = str_to_enum(value + (use_default ? 0 : 1));\n"
" boolean text_found = false;\n"
" for (size_t i = 0; i < p_td.json->nof_enum_texts; ++i) {\n"
" if (strcmp(p_td.json->enum_texts[i].text, value + (use_default ? 0 : 1)) == 0) {\n"
" enum_value = static_cast<%s>(p_td.json->enum_texts[i].index);\n"
" text_found = true;\n"
" break;\n"
" }\n"
" }\n"
" if (!text_found) {\n"
" enum_value = str_to_enum(value + (use_default ? 0 : 1));\n"
" }\n"
" if (!use_default) value[value_len - 1] = '\\\"';\n"
" if (%s == enum_value) {\n"
" error = TRUE;\n"
......@@ -871,7 +896,7 @@ void defEnumClass(const enum_def *edef, output_struct *output)
" }\n"
" return (int)dec_len;\n"
"}\n\n"
, name, unknown_value, enum_type, unbound_value, unbound_value);
, name, enum_type, unknown_value, enum_type, unbound_value, unbound_value);
}
if (oer_needed) {
......
......@@ -27,6 +27,12 @@ JsonSchemaExtension::~JsonSchemaExtension()
Free(value);
}
JsonEnumText::~JsonEnumText()
{
Free(from);
Free(to);
}
void JsonAST::init_JsonAST()
{
omit_as_null = false;
......@@ -53,6 +59,10 @@ JsonAST::JsonAST(const JsonAST *other_val)
}
metainfo_unbound = other_val->metainfo_unbound;
as_map = other_val->as_map;
for (size_t i = 0; i < other_val->enum_texts.size(); ++i) {
enum_texts.add(new JsonEnumText(mcopystr(other_val->enum_texts[i]->from),
mcopystr(other_val->enum_texts[i]->to)));
}
}
}
......@@ -68,6 +78,17 @@ JsonAST::~JsonAST()
free_rawAST_tag_list(tag_list);
delete tag_list;
}
for (size_t i = 0; i < enum_texts.size(); ++i) {
delete enum_texts[i];
}
enum_texts.clear();
}
boolean JsonAST::empty() const
{
return omit_as_null == false && alias == NULL && as_value == false &&
default_value == NULL && metainfo_unbound == false && as_number == false &&
tag_list == NULL && as_map == false && enum_texts.size() == 0;
}
void JsonAST::print_JsonAST() const
......@@ -124,4 +145,11 @@ void JsonAST::print_JsonAST() const
if (as_map) {
printf("Encoding elements into a map of key-value pairs.\n\r");
}
if (0 != enum_texts.size()) {
printf("Enum texts:");
for (size_t i = 0; i < enum_texts.size(); ++i) {
printf(" '%s' -> '%s'", enum_texts[i]->from, enum_texts[i]->to);
}
printf("\n\r");
}
}
......@@ -29,6 +29,14 @@ public:
~JsonSchemaExtension();
};
struct JsonEnumText {
char* from;
char* to;
int index; // set during semantic analysis
JsonEnumText(char* p_from, char* p_to): from(p_from), to(p_to) {}
~JsonEnumText();
};
class JsonAST {
private:
void init_JsonAST();
......@@ -44,11 +52,14 @@ class JsonAST {
boolean as_number;
rawAST_tag_list* tag_list;
boolean as_map;
vector<JsonEnumText> enum_texts;
JsonAST() { init_JsonAST(); }
JsonAST(const JsonAST *other_val);
~JsonAST();
boolean empty() const;
void print_JsonAST() const;
};
......
......@@ -1505,9 +1505,49 @@ XERattribute:
}
| text
{
xerstruct->text_ = (NamespaceSpecification *)Realloc(xerstruct->text_,
++xerstruct->num_text_ * sizeof(NamespaceSpecification));
xerstruct->text_[xerstruct->num_text_-1] = $1;
if (selected_codec == Common::Type::CT_XER || legacy_codec_handling) {
xerstruct->text_ = (NamespaceSpecification *)Realloc(xerstruct->text_,
++xerstruct->num_text_ * sizeof(NamespaceSpecification));
xerstruct->text_[xerstruct->num_text_-1] = $1;
}
if (selected_codec != Common::Type::CT_XER) {
XerAttributes::NameChange special;
special.nn_ = $1.prefix;
switch (special.kw_) {
case NamespaceSpecification::NO_MANGLING:
case NamespaceSpecification::CAPITALIZED:
case NamespaceSpecification::UNCAPITALIZED:
case NamespaceSpecification::UPPERCASED:
case NamespaceSpecification::LOWERCASED:
case NamespaceSpecification::ALL:
if (!legacy_codec_handling &&
selected_codec == Common::Type::CT_JSON) {
Common::Location loc(infile, @$);
loc.error("This format is not supported for the JSON codec");
}
break;
default: // it's a real string
if (selected_codec == Common::Type::CT_JSON ||
legacy_codec_handling) {
if (legacy_codec_handling) {
// in this case the strings are saved in both the XML and JSON
// structs, so we can't use the same strings
jsonstruct->enum_texts.add(
new JsonEnumText(mcopystr($1.prefix), mcopystr($1.uri)));
}
else {
jsonstruct->enum_texts.add(
new JsonEnumText($1.prefix, $1.uri));
}
json_f = true;
}
else {
Free($1.prefix);
Free($1.uri);
}
break;
}
}
}
| XKWuntagged { xerstruct->untagged_ = true; }
| XKWuseNil { xerstruct->useNil_ = true; }
......
......@@ -1405,7 +1405,8 @@ int CHARSTRING::XER_decode(const XERdescriptor_t& p_td, XmlReaderWrap& reader,
depth = reader.Depth();
}
else if ((depth != -1 || omit_tag)
&& (XML_READER_TYPE_TEXT == type || XML_READER_TYPE_CDATA == type || (omit_tag && XML_READER_TYPE_ATTRIBUTE)))
&& (XML_READER_TYPE_TEXT == type || XML_READER_TYPE_CDATA == type ||
(omit_tag && XML_READER_TYPE_ATTRIBUTE == type)))
// Process #text node if we already processed the element node, or
// there is no element node because UNTAGGED is in effect.
{
......
......@@ -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, FALSE };
const TTCN_JSONdescriptor_t INTEGER_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE };
const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL };
......
......@@ -21,6 +21,12 @@ class JSON_Tokenizer;
class CHARSTRING;
class INTEGER;
/** Enumerated text change structure */
struct JsonEnumText {
int index;
const char* text;
};
/** Descriptor for JSON encoding/decoding during runtime */
struct TTCN_JSONdescriptor_t
{
......@@ -73,6 +79,12 @@ struct TTCN_JSONdescriptor_t
* a non-optional universal charstring.
* Example: { "key1" : value1, "key2" : value2 } */
boolean as_map;
/** Number of enumerated values whose texts are changed. */
size_t nof_enum_texts;
/** List of enumerated values whose texts are changed. */
const JsonEnumText* enum_texts;
};
/** This macro makes sure that coding errors will only be displayed if the silent
......
......@@ -5963,7 +5963,7 @@ 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
if (p_td.json->as_map) {
const UNIVERSAL_CHARSTRING* key_ustr = dynamic_cast<
const UNIVERSAL_CHARSTRING*>(get_at(0));
if (NULL == key_ustr) {
......@@ -6016,8 +6016,9 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr,
}
boolean as_value = NULL != p_td.json && p_td.json->as_value;
boolean as_map = NULL != p_td.json && p_td.json->as_map;
int enc_len = as_value ? 0 : p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
int enc_len = (as_value || as_map) ? 0 : p_tok.put_next_token(JSON_TOKEN_OBJECT_START, NULL);
int values_idx = 0;
int edescr_idx = 0;
......@@ -6031,7 +6032,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 (!as_value && NULL != err_vals && NULL != err_vals->before) {
if (!as_value && !as_map && NULL != err_vals && NULL != err_vals->before) {
if (NULL == err_vals->before->errval) {
TTCN_error("internal error: erroneous before value missing");
}
......@@ -6057,27 +6058,56 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr,
if (NULL == err_vals->value->type_descr) {
TTCN_error("internal error: erroneous before typedescriptor missing");
}
// only replace the field's value, keep the field name
if (!as_value) {
enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name);
if (as_map && 0 == i) {
const UNIVERSAL_CHARSTRING* key_ustr = dynamic_cast<
const UNIVERSAL_CHARSTRING*>(err_vals->value->errval);
if (NULL == key_ustr) {
TTCN_EncDec_ErrorContext::error(TTCN_EncDec::ET_UNBOUND,
"Erroneous value for the first field of the 'as map' element type "
"is not a universal charstring");
}
TTCN_Buffer key_buf;
key_ustr->encode_utf8(key_buf);
CHARSTRING key_str;
key_buf.get_string(key_str);
enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, (const char*) key_str);
}
else {
// only replace the field's value, keep the field name
if (!as_value && !as_map) {
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);
}
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 || as_value) {
if (!as_value) {
if (!as_value && !as_map) {
enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, field_name);
}
if (!as_value && metainfo_unbound && !get_at(i)->is_bound()) {
if (!as_value && !as_map && 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);
Free(metainfo_str);
enc_len += p_tok.put_next_token(JSON_TOKEN_STRING, "\"unbound\"");
}
else if (as_map && 0 == i) {
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);
enc_len += p_tok.put_next_token(JSON_TOKEN_NAME, (const char*) key_str);
}
else if (NULL != emb_descr) {
enc_len += get_at(i)->JSON_encode_negtest(emb_descr, *fld_descr(i), p_tok);
} else {
......@@ -6086,7 +6116,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr,
}
}
if (!as_value && NULL != err_vals && NULL != err_vals->after) {
if (!as_value && !as_map && NULL != err_vals && NULL != err_vals->after) {
if (NULL == err_vals->after->errval) {
TTCN_error("internal error: erroneous after value missing");
}
......@@ -6107,7 +6137,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr,
}
}
if (!as_value) {
if (!as_value && !as_map) {
enc_len += p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
}
return enc_len;
......
......@@ -675,9 +675,10 @@ 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}");
// Testing the attribute 'as map'
testcase tc_attribute_as_map() runs on MTC {
var Map x := { { "one", 1 }, { "two", 2 }, { "three", 3 }, { "zero", omit } };
var octetstring enc_exp := char2oct("{\"one\":1,\"two\":2,\"three\":3,\"zero\":null}");
var octetstring enc := f_enc_map(x);
if (enc != enc_exp) {
setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc);
......@@ -689,6 +690,47 @@ testcase tc_attribute_map() runs on MTC {
setverdict(pass);
}
// Testing the attribute 'as map' in a union with the attribute 'as value'
testcase tc_attribute_as_map_in_union() runs on MTC {
var JsonValue x := {
JsonObject := {
{
key := "num",
val := { JsonInteger := 2 }
},
{
key := "str",
val := { JsonString := "abc" }
}
}
};
var octetstring enc_exp := char2oct("{\"num\":2,\"str\":\"abc\"}");
var octetstring enc := f_enc_json_val(x);
if (enc != enc_exp) {
setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc);
}
var JsonValue dec := f_dec_json_val(enc_exp);
if (dec != x) {
setverdict(fail, "Decoding failed. Expected: ", x, ", got: ", dec);
}
setverdict(pass);
}
// Testing the attribute 'text ... as ...'
testcase tc_attribute_text_as() runs on MTC {
var EnumNumberList x := { One, Two, Three };
var octetstring enc_exp := char2oct("[\"1\",\"2\",\"3\"]");
var octetstring enc := f_enc_enum_list(x);
if (enc != enc_exp) {