Commit 5d4b0ac7 authored by Jeno Attila Balasko's avatar Jeno Attila Balasko Committed by Gerrit Code Review
Browse files

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

parents 52faf3ac a06ecc81
......@@ -3004,11 +3004,16 @@ 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) {
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) {
chk_json_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 TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s, %s };\n"
"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, "
"%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"
" 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,10 +1505,50 @@ XERattribute:
}
| text
{
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; }
| XKWuseNumber { xerstruct->useNumber_ = 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");
}
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) {
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);
}
}
}
} 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) {
setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc);
}
var EnumNumberList dec := f_dec_enum_list(enc_exp);
if (dec != x) {
setverdict(fail, "Decoding failed. Expected: ", x, ", got: ", dec);
}