diff --git a/compiler2/Type.cc b/compiler2/Type.cc
index c8534d6d62c8c13083065fabcc40338981c0e1ea..cebbec554629a9634833ac279baf787cb8759046 100644
--- a/compiler2/Type.cc
+++ b/compiler2/Type.cc
@@ -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;
     }
diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc
index 7f55f38c644dffa5e387347ee9ad2f306d8430a4..7c2381d02fa6cb2740c5c6566f596615b3225c29 100644
--- a/compiler2/Type_codegen.cc
+++ b/compiler2/Type_codegen.cc
@@ -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);
diff --git a/compiler2/enum.c b/compiler2/enum.c
index 0036af9e79291eb25445db06d285e20fc11e3c6e..e4d6193a154f8c774494d1440d56016ec3a167e6 100644
--- a/compiler2/enum.c
+++ b/compiler2/enum.c
@@ -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) {
diff --git a/compiler2/ttcn3/JsonAST.cc b/compiler2/ttcn3/JsonAST.cc
index 249996dca8266cf71b3497194e9345dbeb7251a2..71eb8e1462e5e6146f74041b3dd6c37317c0d4e8 100644
--- a/compiler2/ttcn3/JsonAST.cc
+++ b/compiler2/ttcn3/JsonAST.cc
@@ -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");
+  }
 }
diff --git a/compiler2/ttcn3/JsonAST.hh b/compiler2/ttcn3/JsonAST.hh
index 46972383816737ea517fc6c39f9b3b252ca7929a..5669ed0b93596a8e5c5e1a7d390e758f2a6b7270 100644
--- a/compiler2/ttcn3/JsonAST.hh
+++ b/compiler2/ttcn3/JsonAST.hh
@@ -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;
 };
 
diff --git a/compiler2/ttcn3/rawAST.y b/compiler2/ttcn3/rawAST.y
index ff71b62001420d3b02da4cf61d17c2557a3f1853..c6c657a440ae02caf1cfd13a0664b91b52b087ba 100644
--- a/compiler2/ttcn3/rawAST.y
+++ b/compiler2/ttcn3/rawAST.y
@@ -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; }
diff --git a/core/Charstring.cc b/core/Charstring.cc
index 3599d764d08b4ecc9fe1ec7a7d50d4dbf2abaddd..4286b245f74dc2d4a14b5df90365289ba4cb65f2 100644
--- a/core/Charstring.cc
+++ b/core/Charstring.cc
@@ -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.
       {
diff --git a/core/JSON.cc b/core/JSON.cc
index d3295f09ad096a493fcb7dc3f3cfffe10ea07b81..2e0e854eb8c6453ef9bdbd9bb224581b60cd95d1 100644
--- a/core/JSON.cc
+++ b/core/JSON.cc
@@ -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 };
 
 
 
diff --git a/core/JSON.hh b/core/JSON.hh
index 8506128532c4259d299dd00f80198afaf79cf7ec..6cd88edd05d657f581850ce1b92b6089cbb6cb9a 100644
--- a/core/JSON.hh
+++ b/core/JSON.hh
@@ -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
diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc
index 9777189db02f28c9c08a163e0a40d22c7aa5a8c8..fc4b0996c97908f76a919632450d9af2ae00486a 100644
--- a/core2/Basetype2.cc
+++ b/core2/Basetype2.cc
@@ -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;
diff --git a/regression_test/json/AttributeTestcases.ttcn b/regression_test/json/AttributeTestcases.ttcn
index 1485c0d6d44a786e6915ae878cbc79d11b302172..90ef9a4e01548c19573a3d8e2cfaaa8978ec6ffd 100644
--- a/regression_test/json/AttributeTestcases.ttcn
+++ b/regression_test/json/AttributeTestcases.ttcn
@@ -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);
+  }
+  setverdict(pass);
+}
+
 
 control {
   execute(tc_NoAttributeOnUpperLevel())
@@ -718,6 +760,8 @@ control {
   execute(tc_attribute_chosen());
   execute(tc_attribute_chosen_negtest());
   execute(tc_attribute_chosen_default());
-  execute(tc_attribute_map());
+  execute(tc_attribute_as_map());
+  execute(tc_attribute_as_map_in_union());
+  execute(tc_attribute_text_as());
 }
 }
diff --git a/regression_test/json/Functions.ttcn b/regression_test/json/Functions.ttcn
index d877ecd96f3ec2fecab74bbc6f36c7a60299bf1a..003b7b51fd67a47c14e421bf2d2943805780e69d 100644
--- a/regression_test/json/Functions.ttcn
+++ b/regression_test/json/Functions.ttcn
@@ -206,6 +206,12 @@ external function f_enc_multi_list(in MultiLevelList x) return octetstring
 external function f_enc_map(in Map x) return octetstring
   with { extension "prototype(convert) encode(JSON)" }
 
+external function f_enc_json_val(in JsonValue x) return octetstring
+  with { extension "prototype(convert) encode(JSON)" }
+
+external function f_enc_enum_list(in EnumNumberList 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)" }
@@ -411,6 +417,12 @@ external function f_dec_multi_list(in octetstring x) return MultiLevelList
 external function f_dec_map(in octetstring x) return Map
   with { extension "prototype(convert) decode(JSON)" }
 
+external function f_dec_json_val(in octetstring x) return JsonValue
+  with { extension "prototype(convert) decode(JSON)" }
+
+external function f_dec_enum_list(in octetstring x) return EnumNumberList
+  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)" }
diff --git a/regression_test/json/Types.ttcn b/regression_test/json/Types.ttcn
index 15d59c6b4e7ed9acb9cf0f58590f93423eb11676..2bc86553879a93b594ffad85de242194b1dc3902 100644
--- a/regression_test/json/Types.ttcn
+++ b/regression_test/json/Types.ttcn
@@ -364,12 +364,37 @@ with {
 
 type record MapItem {
   universal charstring key,
-  integer value_
+  integer value_ optional
 }
 
 type set of MapItem Map
 with { variant "as map" }
 
+type union JsonValue {
+  integer JsonInteger,
+  float JsonNumber,
+  boolean JsonBool,
+  universal charstring JsonString,
+  record of JsonValue JsonArray,
+  set of record {
+    universal charstring   key,
+    JsonValue               val
+  } JsonObject
+}
+with {
+  variant "JSON: as value"
+  variant (JsonObject) "as map";
+}
+
+type enumerated EnumNumber { One, Two, Three }
+with {
+  variant "text 'One' as '1'";
+  variant "text 'Two' as '2'";
+  variant "text 'Three' as '3'";
+}
+
+type record of EnumNumber EnumNumberList;
+
 } with {
   encode "JSON";
   extension "anytype integer, charstring, R, RoI, Thing";
diff --git a/usrguide/referenceguide/4-ttcn3_language_extensions.adoc b/usrguide/referenceguide/4-ttcn3_language_extensions.adoc
index 1e9471551149639cbb2dac6728199eb1cc1a1332..8e7ca504b3f0a558d9a6cda0482cd4ddabf414ad 100644
--- a/usrguide/referenceguide/4-ttcn3_language_extensions.adoc
+++ b/usrguide/referenceguide/4-ttcn3_language_extensions.adoc
@@ -5678,6 +5678,56 @@ type union Choices {
 
 ----
 
+*As map*
+
+Attribute syntax: as map
+
+Applicable to (TTCN-3): Record of/set of with a record/set element type, that has 2 fields, the first of which is a non-optional universal charstring
+
+Description: If set, the mentioned structure is encoded as a JSON object containing key-value pairs. The universal charstrings in the element records/sets are the keys, and the second field in each record/set contains the value. This allows the creation of heterogenous objects in the JSON document (i.e. JSON objects with any combination of field names and values).
+
+Affects both encoding and decoding.
+
+Example:
+[source]
+----
+type record MapItem {
+  universal charstring key,
+  integer value_ optional
+}
+
+type set of MapItem Map
+with { variant "as map" }
+
+const Map c_map := { { "one", 1 }, { "two", 2 }, { "three", 3 }, { "zero", omit } };
+// is encoded into: { "one" : 1, "two" : 2, "three" : 3, "zero" : null }
+----
+
+*Text ... as ...*
+
+Attribute syntax: text '<enum text>' as '<new text>'
+
+Applicable to (TTCN-3): Enumerated types
+
+Description: This attribute can be used to change the encoding of certain enumerated values. Each attribute changes the encoding of one enumerated option.
+
+Affects both encoding and decoding.
+
+Example:
+[source]
+----
+type enumerated EnumNumber { One, Two, Three }
+with {
+  variant "text 'One' as '1'";
+  variant "text 'Two' as '2'";
+  variant "text 'Three' as '3'";
+}
+type record of EnumNumber EnumNumberList;
+
+const EnumNumberList c_numbers := { One, Two, Three };
+// is encoded into: [ "1", "2", "3" ]
+----
+
 [[external-functions-0]]
 === External functions
 
diff --git a/usrguide/referenceguide/ReferenceGuide.pdf b/usrguide/referenceguide/ReferenceGuide.pdf
index 878057efa8e9847394d6955d2a7f4c5de9b7a349..0047a4001916ae626a011d5791b3b8724d336c98 100644
Binary files a/usrguide/referenceguide/ReferenceGuide.pdf and b/usrguide/referenceguide/ReferenceGuide.pdf differ