From f19a8e12b93ee29aa1e6585a2670ca42c5a8322d Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Thu, 1 Mar 2018 13:15:30 +0100
Subject: [PATCH] Added JSON attribute 'chosen' (bug 528465)

Change-Id: Id3d31c8e61e23465c21b18ab204a714709b5cd60
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/Type.cc                             |  84 +++
 compiler2/Type.hh                             |   3 +-
 compiler2/Type_chk.cc                         |   1 +
 compiler2/Type_codegen.cc                     |  75 ++-
 compiler2/Value.cc                            |   1 +
 compiler2/datatypes.h                         |   1 +
 compiler2/encdec.c                            |   3 +-
 compiler2/enum.c                              |   2 +-
 compiler2/record.c                            | 510 +++++++++++-------
 compiler2/record_of.c                         |   4 +-
 compiler2/ttcn3/JsonAST.cc                    |  27 +
 compiler2/ttcn3/JsonAST.hh                    |   2 +
 compiler2/ttcn3/RawAST.hh                     |   4 +-
 compiler2/ttcn3/rawAST.l                      |   2 +
 compiler2/ttcn3/rawAST.y                      |  44 +-
 compiler2/ttcn3/rawASTspec.h                  |   2 +-
 compiler2/union.c                             |  19 +-
 core/ASN_Null.cc                              |   2 +-
 core/ASN_Null.hh                              |   2 +-
 core/Array.hh                                 |   4 +-
 core/Basetype.cc                              |   2 +-
 core/Basetype.hh                              |   9 +-
 core/Bitstring.cc                             |   2 +-
 core/Bitstring.hh                             |   2 +-
 core/Boolean.cc                               |   2 +-
 core/Boolean.hh                               |   2 +-
 core/Charstring.cc                            |   2 +-
 core/Charstring.hh                            |   2 +-
 core/Float.cc                                 |   2 +-
 core/Float.hh                                 |   2 +-
 core/Hexstring.cc                             |   2 +-
 core/Hexstring.hh                             |   2 +-
 core/Integer.cc                               |   2 +-
 core/Integer.hh                               |   2 +-
 core/JSON.hh                                  |   8 +
 core/Objid.cc                                 |   2 +-
 core/Objid.hh                                 |   2 +-
 core/Octetstring.cc                           |   2 +-
 core/Octetstring.hh                           |   2 +-
 core/Optional.hh                              |  25 +-
 core/TTCN3.hh                                 |   2 +-
 core/Universal_charstring.cc                  |   2 +-
 core/Universal_charstring.hh                  |   2 +-
 core/Verdicttype.cc                           |   2 +-
 core/Verdicttype.hh                           |   2 +-
 core2/Basetype2.cc                            |   6 +-
 .../Semantic_Analyser/Makefile.semantic       |   3 +-
 .../Semantic_Analyser/json/.gitignore         |   2 +
 .../Semantic_Analyser/json/JsonChosen_SE.ttcn |  38 ++
 function_test/Semantic_Analyser/json/Makefile |  12 +
 function_test/Semantic_Analyser/json/t        |   9 +
 regression_test/json/AttributeTestcases.ttcn  | 108 ++++
 regression_test/json/Functions.ttcn           |   3 +
 regression_test/json/Types.ttcn               |  33 ++
 54 files changed, 839 insertions(+), 253 deletions(-)
 create mode 100644 function_test/Semantic_Analyser/json/.gitignore
 create mode 100644 function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn
 create mode 100644 function_test/Semantic_Analyser/json/Makefile
 create mode 100755 function_test/Semantic_Analyser/json/t

diff --git a/compiler2/Type.cc b/compiler2/Type.cc
index e1c83c741..9cd8d936a 100644
--- a/compiler2/Type.cc
+++ b/compiler2/Type.cc
@@ -52,6 +52,7 @@
 #include "ttcn3/TtcnTemplate.hh"
 #include "ttcn3/Templatestuff.hh"
 #include "ttcn3/RawAST.hh"
+#include "ttcn3/JsonAST.hh"
 
 #include "../common/static_check.h"
 #include "PredefFunc.hh"
@@ -2155,6 +2156,10 @@ namespace Common {
         size_t fieldnum;
         for(int c=0;c<rawattrib->taglist.nElements;c++) { // check TAG
           Identifier *idf=rawattrib->taglist.tag[c].fieldName;
+          if (idf == NULL) {
+            error("Field member in RAW parameter TAG cannot be 'omit'");
+            continue;
+          }
           if(!has_comp_withName(*idf)){
             error("Invalid field name `%s' in RAW parameter TAG "
               "for type `%s'", idf->get_dispname().c_str(),
@@ -2488,6 +2493,10 @@ namespace Common {
                     ,field_id.get_dispname().c_str());
               break;
             }
+            if (idf == NULL) {
+              error("Field member in RAW parameter CROSSTAG cannot be 'omit'");
+              break;
+            }
             if(!field_type_last->has_comp_withName(*idf)){
               error("Invalid fieldmember name in RAW parameter CROSSTAG"
                     " for field %s: %s"
@@ -2971,6 +2980,10 @@ namespace Common {
         error("Invalid attribute, 'as number' is only allowed for enumerated "
           "types");
       }
+      
+      if (NULL != jsonattrib->tag_list) {
+        chk_json_tag_list();
+      }
     }
   }
   
@@ -3178,6 +3191,77 @@ namespace Common {
     }
   }
   
+  void Type::chk_json_tag_list()
+  {
+    Type* last = get_type_refd_last();
+    Type* parent = get_parent_type();
+    if (parent == NULL || last->get_typetype_ttcn3() != T_CHOICE_T ||
+        (parent->typetype != T_SEQ_T && parent->typetype != T_SET_T)) {
+      error("Invalid attribute, 'chosen' is only allowed for fields of records "
+        "and sets of union type");
+      return;
+    }
+    
+    rawAST_tag_list* tag_list = jsonattrib->tag_list;
+    for (int i = 0; i < tag_list->nElements; ++i) {
+      Identifier* union_field_id = tag_list->tag[i].fieldName;
+      if (union_field_id == NULL) {
+        if (!is_optional_field()) {
+          error("Target of JSON attribute 'chosen' is a mandatory field and "
+            "cannot be set to 'omit'");
+          continue;
+        }
+      }
+      else if (!has_comp_withName(*union_field_id)) {
+        error("Reference to invalid union field name `%s' for type `%s', in JSON "
+          "attribute 'chosen'",
+          union_field_id->get_dispname().c_str(), get_typename().c_str());
+        continue;
+      }
+      
+      for (int j = 0; j < tag_list->tag[i].nElements; ++j) {
+        bool erroneous = false;
+        Type* current_type = parent; // the first field name refers to the parent type
+        for (int k = 0; k < tag_list->tag[i].keyList[j].keyField->nElements; ++k) {
+          if (!current_type->is_secho()) {
+            error("Too many field references in JSON attribute 'chosen'. "
+              "Type `%s' doesn't have fields.",
+              current_type->get_typename().c_str());
+            erroneous = true;
+            break;
+          }
+          Identifier* current_field_id = tag_list->tag[i].keyList[j].keyField->names[k];
+          if (!current_type->has_comp_withName(*current_field_id)) {
+            error("Reference to invalid field name `%s' for type `%s', "
+              "in JSON attribute 'chosen'",
+              current_field_id->get_dispname().c_str(),
+              current_type->get_typename().c_str());
+            erroneous = true;
+            break;
+          }
+          CompField* current_field = current_type->get_comp_byName(*current_field_id);
+          current_type = current_field->get_type()->get_type_refd_last();
+        }
+        if (!erroneous) {
+          Error_Context cntx(this, "In JSON attribute 'choice'");
+          Value* value = tag_list->tag[i].keyList[j].v_value;
+          value->set_my_scope(get_my_scope());
+          value->set_my_governor(current_type);
+          current_type->chk_this_value_ref(value);
+          current_type->chk_this_value(value, 0, EXPECTED_CONSTANT,
+            INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK);
+          
+          Value::valuetype_t value_type = value->get_valuetype();
+          if (value_type == Value::V_ENUM || value_type == Value::V_REFD) {
+            Free(tag_list->tag[i].keyList[j].value);
+            tag_list->tag[i].keyList[j].value =
+              mcopystr(value->get_single_expr().c_str());
+          }
+        }
+      }
+    }
+  }
+  
   void Type::force_json() 
   {
     if (!jsonattrib)
diff --git a/compiler2/Type.hh b/compiler2/Type.hh
index de9bfd29f..cd3ac9e58 100644
--- a/compiler2/Type.hh
+++ b/compiler2/Type.hh
@@ -36,12 +36,12 @@
 #include "ttcn3/rawASTspec.h"
 #include "ttcn3/TextAST.hh"
 #include "ttcn3/BerAST.hh"
-#include "ttcn3/JsonAST.hh"
 #include "ttcn3/OerAST.hh"
 #include <float.h>
 
 class XerAttributes;
 class RawAST;
+class JsonAST;
 enum namedbool { INCOMPLETE_NOT_ALLOWED = 0, INCOMPLETE_ALLOWED = 1, WARNING_FOR_INCOMPLETE = 2,
   NO_SUB_CHK = 0, SUB_CHK = 3,
   OMIT_NOT_ALLOWED = 0, OMIT_ALLOWED = 4,
@@ -897,6 +897,7 @@ namespace Common {
     
     void chk_json();
     void chk_json_default();
+    void chk_json_tag_list();
     /** If the type does not have a jsonattrib, create one. */
     void force_json();
 
diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc
index a67fc78ae..f93130c37 100644
--- a/compiler2/Type_chk.cc
+++ b/compiler2/Type_chk.cc
@@ -50,6 +50,7 @@
 #include "asn1/Tag.hh"
 #include "XerAttributes.hh"
 #include "ttcn3/RawAST.hh"
+#include "ttcn3/JsonAST.hh"
 
 #include <ctype.h>
 #include <stdlib.h> // for qsort
diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc
index 8add56f5d..2e7867851 100644
--- a/compiler2/Type_codegen.cc
+++ b/compiler2/Type_codegen.cc
@@ -37,6 +37,7 @@
 #include "ttcn3/signature.h"
 #include "XerAttributes.hh"
 #include "ttcn3/RawAST.hh"
+#include "ttcn3/JsonAST.hh"
 
 #include "asn1/TableConstraint.hh"
 #include "asn1/Object.hh"
@@ -1071,7 +1072,7 @@ void Type::generate_code_jsondescriptor(output_struct *target)
       , get_genname_own().c_str() 
       , jsonattrib->omit_as_null ? "TRUE" : "FALSE"
       , alias ? alias : "NULL"
-      , jsonattrib->as_value ? "TRUE" : "FALSE"
+      , (jsonattrib->as_value || jsonattrib->tag_list != NULL) ? "TRUE" : "FALSE"
       , def_val ? def_val : "NULL"
       , jsonattrib->metainfo_unbound ? "TRUE" : "FALSE"
       , jsonattrib->as_number ? "TRUE" : "FALSE");
@@ -1797,6 +1798,67 @@ void Type::generate_code_Se(output_struct *target)
       cur.jsonAlias = type->jsonattrib->alias;
       cur.jsonDefaultValue = type->jsonattrib->default_value;
       cur.jsonMetainfoUnbound = type->jsonattrib->metainfo_unbound;
+      if (type->jsonattrib->tag_list != NULL) {
+        rawAST_tag_list* tag_list = type->jsonattrib->tag_list;
+        sdef.elements[i].jsonChosen = (rawAST_coding_taglist_list*)
+          Malloc(sizeof(rawAST_coding_taglist_list));
+        sdef.elements[i].jsonChosen->nElements = tag_list->nElements;
+        sdef.elements[i].jsonChosen->list = (rawAST_coding_taglist*)
+          Malloc(tag_list->nElements * sizeof(rawAST_coding_taglist));
+        for (int c = 0; c < tag_list->nElements; ++c) {
+          if (tag_list->tag[c].nElements != 0) {
+            sdef.elements[i].jsonChosen->list[c].fields =
+              (rawAST_coding_field_list*)Malloc(tag_list->tag[c].nElements *
+              sizeof(rawAST_coding_field_list));
+          }
+          else {
+            sdef.elements[i].jsonChosen->list[c].fields = NULL;
+          }
+          sdef.elements[i].jsonChosen->list[c].nElements =
+            tag_list->tag[c].nElements;
+          Identifier* union_field_id = tag_list->tag[c].fieldName;
+          sdef.elements[i].jsonChosen->list[c].fieldName = union_field_id != NULL ?
+            union_field_id->get_name().c_str() : NULL; // TODO: currently unused
+          sdef.elements[i].jsonChosen->list[c].fieldnum = union_field_id != NULL ?
+            type->get_type_refd_last()->get_comp_index_byName(
+            *tag_list->tag[c].fieldName) : -2;
+          for (int a = 0; a <tag_list->tag[c].nElements; ++a) {
+            rawAST_coding_field_list* key =
+              sdef.elements[i].jsonChosen->list[c].fields + a;
+            key->nElements = tag_list->tag[c].keyList[a].keyField->nElements;
+            key->value = tag_list->tag[c].keyList[a].value;
+            key->fields = (rawAST_coding_fields*)
+              Malloc(key->nElements * sizeof(rawAST_coding_fields));
+            Type *t = this;
+            for (int b = 0; b < key->nElements; ++b) {
+              Identifier* current_field_id =
+                tag_list->tag[c].keyList[a].keyField->names[b];
+              size_t current_field_index = t->get_comp_index_byName(*current_field_id);
+              CompField* current_field = t->get_comp_byIndex(current_field_index);
+              key->fields[b].nthfield = current_field_index;
+              key->fields[b].nthfieldname = current_field_id->get_name().c_str();
+              if (t->typetype == T_CHOICE_T) {
+                key->fields[b].fieldtype = UNION_FIELD;
+              }
+              else if (current_field->get_is_optional()) {
+                key->fields[b].fieldtype = OPTIONAL_FIELD;
+              }
+              else {
+                key->fields[b].fieldtype = MANDATORY_FIELD;
+              }
+              Type *field_type = current_field->get_type();
+              key->fields[b].type =
+                pool.add(field_type->get_genname_value(my_scope));
+              key->fields[b].typedescr =
+                pool.add(field_type->get_genname_typedescriptor(my_scope));
+              t = field_type->get_type_refd_last();
+            }
+          }
+        }
+      }
+      else {
+        sdef.elements[i].jsonChosen = NULL;
+      }
     } // if jsonattrib
   } // next element
 
@@ -2085,6 +2147,17 @@ void Type::generate_code_Se(output_struct *target)
   for(size_t i = 0; i < sdef.totalElements; i++) {
     // free the array but not the strings
     if (sdef.elements[i].xerAnyNum > 0) Free(sdef.elements[i].xerAnyUris);
+    
+    if (sdef.elements[i].jsonChosen != NULL) {
+      for (int j = 0; j < sdef.elements[i].jsonChosen->nElements; ++j) {
+        for (int k = 0; k < sdef.elements[i].jsonChosen->list[j].nElements; ++k) {
+          Free(sdef.elements[i].jsonChosen->list[j].fields[k].fields);
+        }
+        Free(sdef.elements[i].jsonChosen->list[j].fields);
+      }
+      Free(sdef.elements[i].jsonChosen->list);
+      Free(sdef.elements[i].jsonChosen);
+    }
   } // next i
 
   if (sdef.hasRaw) {
diff --git a/compiler2/Value.cc b/compiler2/Value.cc
index 2c641816c..c4a679e1e 100644
--- a/compiler2/Value.cc
+++ b/compiler2/Value.cc
@@ -53,6 +53,7 @@
 #include "ttcn3/Statement.hh"
 
 #include "ttcn3/Attributes.hh"
+#include "ttcn3/JsonAST.hh"
 #include "../common/JSON_Tokenizer.hh"
 #include "ttcn3/Ttcn2Json.hh"
 
diff --git a/compiler2/datatypes.h b/compiler2/datatypes.h
index f85fd171c..a37a9a08e 100644
--- a/compiler2/datatypes.h
+++ b/compiler2/datatypes.h
@@ -87,6 +87,7 @@ typedef struct {
   boolean jsonMetainfoUnbound;
   const char* jsonAlias;
   const char* jsonDefaultValue;
+  rawAST_coding_taglist_list* jsonChosen;
   /** true if the field is a record-of or set-of with optimized memory allocation */
   boolean optimizedMemAlloc;
   XSD_types xsd_type;
diff --git a/compiler2/encdec.c b/compiler2/encdec.c
index a46403366..52a96ef66 100644
--- a/compiler2/encdec.c
+++ b/compiler2/encdec.c
@@ -81,7 +81,8 @@ void def_encdec(const char *p_classname,
   if(json) {
     def = mputprintf(def,
       "int JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&) const;\n"
-      "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);\n");
+      "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, "
+      "int p_chosen_field = CHOSEN_FIELD_UNSET);\n");
   }
   if(oer) {
     def = mputprintf(def,
diff --git a/compiler2/enum.c b/compiler2/enum.c
index 7e963abc8..79099bf70 100644
--- a/compiler2/enum.c
+++ b/compiler2/enum.c
@@ -809,7 +809,7 @@ void defEnumClass(const enum_def *edef, output_struct *output)
     
     // JSON decode
     src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n"
+      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n"
       "{\n"
       "  json_token_t token = JSON_TOKEN_NONE;\n"
       "  char* value = 0;\n"
diff --git a/compiler2/record.c b/compiler2/record.c
index 35dc461e2..a66dadee4 100644
--- a/compiler2/record.c
+++ b/compiler2/record.c
@@ -3188,6 +3188,294 @@ void gen_xer(const struct_def *sdef, char **pdef, char **psrc)
   *psrc = src;
 }
 
+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" : "");
+
+  if (sdef->nElements == 1) {
+    if (!sdef->jsonAsValue) {
+      src = mputstr(src, "  if (NULL != p_td.json && p_td.json->as_value) {\n");
+    }
+    src = mputprintf(src, "  %sreturn field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n",
+      sdef->jsonAsValue ? "" : "  ", sdef->elements[0].name, sdef->elements[0].typedescrname);
+    if (!sdef->jsonAsValue) {
+      src = mputstr(src, "  }\n");
+    }
+  }
+  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"
+      "    return JSON_ERROR_FATAL;\n"
+      "  }\n"
+      "  else if (JSON_TOKEN_OBJECT_START != j_token) {\n"
+      "    return JSON_ERROR_INVALID_TOKEN;\n"
+      "  }\n");
+
+    boolean has_metainfo_enabled = FALSE;
+    for (int i = 0; i < sdef->nElements; ++i) {
+      src = mputprintf(src, "  boolean %s_found = FALSE;\n", sdef->elements[i].name);
+      if (sdef->elements[i].jsonMetainfoUnbound) {
+        // initialize meta info states
+        src = mputprintf(src, 
+          "  int metainfo_%s = JSON_METAINFO_NONE;\n"
+          , sdef->elements[i].name);
+        has_metainfo_enabled = TRUE;
+      }
+    }
+    src = mputstr(src,
+      // Read name - value token pairs until we reach some other token
+      "\n  while (TRUE) {\n"
+      "    char* fld_name = 0;\n"
+      "    size_t name_len = 0;\n"
+      "    size_t buf_pos = p_tok.get_buf_pos();\n"
+      "    dec_len += p_tok.get_next_token(&j_token, &fld_name, &name_len);\n"
+      "    if (JSON_TOKEN_ERROR == j_token) {\n"
+      "      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_NAME_TOKEN_ERROR);\n"
+      "      return JSON_ERROR_FATAL;\n"
+      "    }\n"
+           // undo the last action on the buffer
+      "    else if (JSON_TOKEN_NAME != j_token) {\n"
+      "      p_tok.set_buf_pos(buf_pos);\n"
+      "      break;\n"
+      "    }\n"
+      "    else {\n      ");
+    if (has_metainfo_enabled) {
+      // check for meta info
+      src = mputstr(src,
+        "boolean is_metainfo = FALSE;\n"
+        "      if (name_len > 9 && 0 == strncmp(fld_name, \"metainfo \", 9)) {\n"
+        "        fld_name += 9;\n"
+        "        name_len -= 9;\n"
+        "        is_metainfo = TRUE;\n"
+        "      }\n      ");
+    }
+    for (int i = 0; i < sdef->nElements; ++i) {
+      src = mputprintf(src,
+        // check field name
+        "if (%d == name_len && 0 == strncmp(fld_name, \"%s\", name_len)) {\n"
+        "        %s_found = TRUE;\n"
+        , (int)strlen(sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname)
+        , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname
+        , sdef->elements[i].name);
+      if (has_metainfo_enabled) {
+        src = mputstr(src, "        if (is_metainfo) {\n");
+        if (sdef->elements[i].jsonMetainfoUnbound) {
+          src = mputprintf(src,
+            // check meta info
+            "          char* info_value = 0;\n"
+            "          size_t info_len = 0;\n"
+            "          dec_len += p_tok.get_next_token(&j_token, &info_value, &info_len);\n"
+            "          if (JSON_TOKEN_STRING == j_token && 9 == info_len &&\n"
+            "              0 == strncmp(info_value, \"\\\"unbound\\\"\", 9)) {\n"
+            "            metainfo_%s = JSON_METAINFO_UNBOUND;\n"
+            "          }\n"
+            "          else {\n"
+            "            JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_VALUE_ERROR, \"%s\");\n"
+            "            return JSON_ERROR_FATAL;\n"
+            "          }\n"
+            , sdef->elements[i].name, sdef->elements[i].dispname);
+        }
+        else {
+          src = mputprintf(src,
+            "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_NOT_APPLICABLE, \"%s\");\n"
+            "          return JSON_ERROR_FATAL;\n"
+            , sdef->elements[i].dispname);
+        }
+        src = mputstr(src,
+          "        }\n"
+          "        else {\n");
+        if (sdef->elements[i].jsonMetainfoUnbound) {
+          src = mputstr(src, "         buf_pos = p_tok.get_buf_pos();\n");
+        }
+      }
+      if (sdef->elements[i].jsonChosen != NULL) {
+        /* field index of the otherwise rule */
+        char* otherwise_str = NULL;
+        boolean first_value = TRUE;
+        src = mputstr(src, "         int chosen_field = CHOSEN_FIELD_UNSET;\n");
+        int j;
+        for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
+          rawAST_coding_taglist* cur_choice =
+            sdef->elements[i].jsonChosen->list + j;
+          if (cur_choice->nElements > 0) {
+            /* this is a normal rule */
+            if (first_value) {
+              src = mputstr(src, "         if (");
+              first_value = FALSE;
+            }
+            else {
+              src = mputstr(src, "         else if (");
+            }
+            src = genRawFieldChecker(src, cur_choice, TRUE);
+            /* set chosen_field in the if's body */
+            src = mputstr(src, ") {\n"
+              "           chosen_field = ");
+            if (cur_choice->fieldnum != -2) {
+              src = mputprintf(src, "%d", cur_choice->fieldnum);
+            }
+            else {
+              src = mputstr(src, "CHOSEN_FIELD_OMITTED");
+            }
+            src = mputstr(src, ";\n"
+              "         }\n");
+          }
+          else {
+            /* this is an otherwise rule */
+            otherwise_str = cur_choice->fieldnum != -2 ?
+              mprintf("%d", cur_choice->fieldnum) : mcopystr("CHOSEN_FIELD_OMITTED");
+          }
+        }
+        if (otherwise_str != NULL) {
+          /* set chosen_field to the field index of the otherwise rule or -1 */
+          src = mputprintf(src,
+            "         else {\n"
+            "           chosen_field = %s;\n"
+            "         }\n", otherwise_str);
+          Free(otherwise_str);
+        }
+      }
+      src = mputprintf(src,
+        "         int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent%s);\n"
+        "         if (0 > ret_val) {\n"
+        "           if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n"
+        , sdef->elements[i].name, sdef->elements[i].typedescrname
+        , sdef->elements[i].jsonChosen != NULL ? ", chosen_field" : "");
+      if (sdef->elements[i].jsonMetainfoUnbound) {
+        src = mputprintf(src,
+          // undo the last action on the buffer, check if the invalid token was a null token 
+          "             p_tok.set_buf_pos(buf_pos);\n"
+          "             p_tok.get_next_token(&j_token, NULL, NULL);\n"
+          "             if (JSON_TOKEN_LITERAL_NULL == j_token) {\n"
+          "               if (JSON_METAINFO_NONE == metainfo_%s) {\n"
+          // delay reporting an error for now, there might be meta info later
+          "                 metainfo_%s = JSON_METAINFO_NEEDED;\n"
+          "                 continue;\n"
+          "               }\n"
+          "               else if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
+          // meta info already found
+          "                 continue;\n"
+          "               }\n"
+          "             }\n"
+          , sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name);
+      }
+      src = mputprintf(src,
+        "             JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
+        "           }\n"
+        "           return JSON_ERROR_FATAL;\n"
+        "         }\n"
+        "         dec_len += (size_t)ret_val;\n"
+        , (unsigned long) strlen(sdef->elements[i].dispname), sdef->elements[i].dispname);
+      if (has_metainfo_enabled) {
+        src = mputstr(src, "        }\n");
+      }
+      src = mputstr(src,
+        "      }\n"
+        "      else ");
+    }
+    src = mputprintf(src,
+      "{\n"
+               // invalid field name
+      "        JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, %sJSON_DEC_INVALID_NAME_ERROR, (int)name_len, fld_name);\n"
+               // if this is set to a warning, skip the value of the field
+      "        dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
+      "        if (JSON_TOKEN_NUMBER != j_token && JSON_TOKEN_STRING != j_token &&\n"
+      "            JSON_TOKEN_LITERAL_TRUE != j_token && JSON_TOKEN_LITERAL_FALSE != j_token &&\n"
+      "            JSON_TOKEN_LITERAL_NULL != j_token) {\n"
+      "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, (int)name_len, fld_name);\n"
+      "          return JSON_ERROR_FATAL;\n"
+      "        }\n"
+      "      }\n"
+      "    }\n"
+      "  }\n\n"
+      "  dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
+      "  if (JSON_TOKEN_OBJECT_END != j_token) {\n"
+      "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_OBJECT_END_TOKEN_ERROR, \"\");\n"
+      "    return JSON_ERROR_FATAL;\n"
+      "  }\n\n  "
+      , has_metainfo_enabled ? "is_metainfo ?\n          JSON_DEC_METAINFO_NAME_ERROR : " : "");
+    // Check if every field has been set and handle meta info
+    for (int i = 0; i < sdef->nElements; ++i) {
+      if (sdef->elements[i].jsonMetainfoUnbound) {
+        src = mputprintf(src,
+          "if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
+          "    field_%s.clean_up();\n"
+          "  }\n"
+          "  else if (JSON_METAINFO_NEEDED == metainfo_%s) {\n"
+          // no meta info was found for this field, report the delayed error
+          "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
+          "  }\n"
+          "  else "
+          , sdef->elements[i].name, sdef->elements[i].name
+          , sdef->elements[i].name
+          , (unsigned long) strlen(sdef->elements[i].dispname)
+          , sdef->elements[i].dispname);
+      }
+      src = mputprintf(src,
+        "if (!%s_found) {\n"
+        , sdef->elements[i].name);
+      if (sdef->elements[i].jsonDefaultValue) {
+        src = mputprintf(src,
+          "    field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n"
+          , sdef->elements[i].name, sdef->elements[i].typedescrname);
+      }
+      else if (sdef->elements[i].isOptional) {
+        // if the conditions in attribute 'choice' indicate that this field is
+        // mandatory, then display an error
+        if (sdef->elements[i].jsonChosen != NULL) {
+          int j;
+          boolean has_otherwise = FALSE;
+          for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
+            if (sdef->elements[i].jsonChosen->list[j].nElements == 0) {
+              has_otherwise = TRUE;
+              break;
+            }
+          }
+          boolean first_found = FALSE;
+          for (j = 0; j < sdef->elements[i].jsonChosen->nElements; j++) {
+            if ((!has_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum != -2) ||
+                 (has_otherwise && sdef->elements[i].jsonChosen->list[j].fieldnum == -2)) {
+              if (!first_found) {
+                src = mputstr(src, "    if (");
+                first_found = TRUE;
+              }
+              else {
+                src = mputstr(src, "\n        || ");
+              }
+              src = genRawFieldChecker(src, sdef->elements[i].jsonChosen->list + j, !has_otherwise);
+            }
+          }
+          if (first_found) {
+            src = mputprintf(src,
+              ") {\n"
+              "      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED, \"%s\");\n"
+              "      return JSON_ERROR_FATAL;\n"
+              "    }\n", sdef->elements[i].dispname);
+          }
+        }
+        src = mputprintf(src,
+          "    field_%s = OMIT_VALUE;\n"
+          , sdef->elements[i].name);
+      } else {
+        src = mputprintf(src,
+          "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_MISSING_FIELD_ERROR, \"%s\");\n"
+          "    return JSON_ERROR_FATAL;\n"
+          , sdef->elements[i].dispname);
+      }
+      src = mputstr(src,
+        "  }\n  ");
+    }
+    src = mputstr(src,
+      "\n  return (int)dec_len;\n");
+  }
+  return mputstr(src, "}\n\n");
+}
+
 void defRecordClass1(const struct_def *sdef, output_struct *output)
 {
   size_t i;
@@ -4447,209 +4735,7 @@ void defRecordClass1(const struct_def *sdef, output_struct *output)
     src = mputstr(src, "}\n\n");
     
     // JSON decode, RT1
-    src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n"
-      "{\n", name, (sdef->nElements == 1 && !sdef->jsonAsValue) ? " p_td" : "");
-    
-    if (sdef->nElements == 1) {
-      if (!sdef->jsonAsValue) {
-        src = mputstr(src, "  if (NULL != p_td.json && p_td.json->as_value) {\n");
-      }
-      src = mputprintf(src, "  %sreturn field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n",
-        sdef->jsonAsValue ? "" : "  ", sdef->elements[0].name, sdef->elements[0].typedescrname);
-      if (!sdef->jsonAsValue) {
-        src = mputstr(src, "  }\n");
-      }
-    }
-    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"
-        "    return JSON_ERROR_FATAL;\n"
-        "  }\n"
-        "  else if (JSON_TOKEN_OBJECT_START != j_token) {\n"
-        "    return JSON_ERROR_INVALID_TOKEN;\n"
-        "  }\n");
-
-      boolean has_metainfo_enabled = FALSE;
-      for (i = 0; i < sdef->nElements; ++i) {
-        src = mputprintf(src, "  boolean %s_found = FALSE;\n", sdef->elements[i].name);
-        if (sdef->elements[i].jsonMetainfoUnbound) {
-          // initialize meta info states
-          src = mputprintf(src, 
-            "  int metainfo_%s = JSON_METAINFO_NONE;\n"
-            , sdef->elements[i].name);
-          has_metainfo_enabled = TRUE;
-        }
-      }
-      src = mputstr(src,
-        // Read name - value token pairs until we reach some other token
-        "\n  while (TRUE) {\n"
-        "    char* fld_name = 0;\n"
-        "    size_t name_len = 0;\n"
-        "    size_t buf_pos = p_tok.get_buf_pos();\n"
-        "    dec_len += p_tok.get_next_token(&j_token, &fld_name, &name_len);\n"
-        "    if (JSON_TOKEN_ERROR == j_token) {\n"
-        "      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_NAME_TOKEN_ERROR);\n"
-        "      return JSON_ERROR_FATAL;\n"
-        "    }\n"
-             // undo the last action on the buffer
-        "    else if (JSON_TOKEN_NAME != j_token) {\n"
-        "      p_tok.set_buf_pos(buf_pos);\n"
-        "      break;\n"
-        "    }\n"
-        "    else {\n      ");
-      if (has_metainfo_enabled) {
-        // check for meta info
-        src = mputstr(src,
-          "boolean is_metainfo = FALSE;\n"
-          "      if (name_len > 9 && 0 == strncmp(fld_name, \"metainfo \", 9)) {\n"
-          "        fld_name += 9;\n"
-          "        name_len -= 9;\n"
-          "        is_metainfo = TRUE;\n"
-          "      }\n      ");
-      }
-      for (i = 0; i < sdef->nElements; ++i) {
-        src = mputprintf(src,
-          // check field name
-          "if (%d == name_len && 0 == strncmp(fld_name, \"%s\", name_len)) {\n"
-          "        %s_found = TRUE;\n"
-          , (int)strlen(sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname)
-          , sdef->elements[i].jsonAlias ? sdef->elements[i].jsonAlias : sdef->elements[i].dispname
-          , sdef->elements[i].name);
-        if (has_metainfo_enabled) {
-          src = mputstr(src, "        if (is_metainfo) {\n");
-          if (sdef->elements[i].jsonMetainfoUnbound) {
-            src = mputprintf(src,
-              // check meta info
-              "          char* info_value = 0;\n"
-              "          size_t info_len = 0;\n"
-              "          dec_len += p_tok.get_next_token(&j_token, &info_value, &info_len);\n"
-              "          if (JSON_TOKEN_STRING == j_token && 9 == info_len &&\n"
-              "              0 == strncmp(info_value, \"\\\"unbound\\\"\", 9)) {\n"
-              "            metainfo_%s = JSON_METAINFO_UNBOUND;\n"
-              "          }\n"
-              "          else {\n"
-              "            JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_VALUE_ERROR, \"%s\");\n"
-              "            return JSON_ERROR_FATAL;\n"
-              "          }\n"
-              , sdef->elements[i].name, sdef->elements[i].dispname);
-          }
-          else {
-            src = mputprintf(src,
-              "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_METAINFO_NOT_APPLICABLE, \"%s\");\n"
-              "          return JSON_ERROR_FATAL;\n"
-              , sdef->elements[i].dispname);
-          }
-          src = mputstr(src,
-            "        }\n"
-            "        else {\n");
-          if (sdef->elements[i].jsonMetainfoUnbound) {
-            src = mputstr(src, "         buf_pos = p_tok.get_buf_pos();\n");
-          }
-        }
-        src = mputprintf(src,
-          "         int ret_val = field_%s.JSON_decode(%s_descr_, p_tok, p_silent);\n"
-          "         if (0 > ret_val) {\n"
-          "           if (JSON_ERROR_INVALID_TOKEN == ret_val) {\n"
-          , sdef->elements[i].name, sdef->elements[i].typedescrname);
-        if (sdef->elements[i].jsonMetainfoUnbound) {
-          src = mputprintf(src,
-            // undo the last action on the buffer, check if the invalid token was a null token 
-            "             p_tok.set_buf_pos(buf_pos);\n"
-            "             p_tok.get_next_token(&j_token, NULL, NULL);\n"
-            "             if (JSON_TOKEN_LITERAL_NULL == j_token) {\n"
-            "               if (JSON_METAINFO_NONE == metainfo_%s) {\n"
-            // delay reporting an error for now, there might be meta info later
-            "                 metainfo_%s = JSON_METAINFO_NEEDED;\n"
-            "                 continue;\n"
-            "               }\n"
-            "               else if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
-            // meta info already found
-            "                 continue;\n"
-            "               }\n"
-            "             }\n"
-            , sdef->elements[i].name, sdef->elements[i].name, sdef->elements[i].name);
-        }
-        src = mputprintf(src,
-          "             JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
-          "           }\n"
-          "           return JSON_ERROR_FATAL;\n"
-          "         }\n"
-          "         dec_len += (size_t)ret_val;\n"
-          , (unsigned long) strlen(sdef->elements[i].dispname), sdef->elements[i].dispname);
-        if (has_metainfo_enabled) {
-          src = mputstr(src, "        }\n");
-        }
-        src = mputstr(src,
-          "      }\n"
-          "      else ");
-      }
-      src = mputprintf(src,
-        "{\n"
-                 // invalid field name
-        "        JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, %sJSON_DEC_INVALID_NAME_ERROR, (int)name_len, fld_name);\n"
-                 // if this is set to a warning, skip the value of the field
-        "        dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
-        "        if (JSON_TOKEN_NUMBER != j_token && JSON_TOKEN_STRING != j_token &&\n"
-        "            JSON_TOKEN_LITERAL_TRUE != j_token && JSON_TOKEN_LITERAL_FALSE != j_token &&\n"
-        "            JSON_TOKEN_LITERAL_NULL != j_token) {\n"
-        "          JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, (int)name_len, fld_name);\n"
-        "          return JSON_ERROR_FATAL;\n"
-        "        }\n"
-        "      }\n"
-        "    }\n"
-        "  }\n\n"
-        "  dec_len += p_tok.get_next_token(&j_token, NULL, NULL);\n"
-        "  if (JSON_TOKEN_OBJECT_END != j_token) {\n"
-        "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_OBJECT_END_TOKEN_ERROR, \"\");\n"
-        "    return JSON_ERROR_FATAL;\n"
-        "  }\n\n  "
-        , has_metainfo_enabled ? "is_metainfo ?\n          JSON_DEC_METAINFO_NAME_ERROR : " : "");
-      // Check if every field has been set and handle meta info
-      for (i = 0; i < sdef->nElements; ++i) {
-        if (sdef->elements[i].jsonMetainfoUnbound) {
-          src = mputprintf(src,
-            "if (JSON_METAINFO_UNBOUND == metainfo_%s) {\n"
-            "    field_%s.clean_up();\n"
-            "  }\n"
-            "  else if (JSON_METAINFO_NEEDED == metainfo_%s) {\n"
-            // no meta info was found for this field, report the delayed error
-            "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_FIELD_TOKEN_ERROR, %lu, \"%s\");\n"
-            "  }\n"
-            "  else "
-            , sdef->elements[i].name, sdef->elements[i].name
-            , sdef->elements[i].name
-            , (unsigned long) strlen(sdef->elements[i].dispname)
-            , sdef->elements[i].dispname);
-        }
-        src = mputprintf(src,
-          "  if (!%s_found) {\n"
-          , sdef->elements[i].name);
-        if (sdef->elements[i].jsonDefaultValue) {
-          src = mputprintf(src,
-            "    field_%s.JSON_decode(%s_descr_, DUMMY_BUFFER, p_silent);\n"
-            , sdef->elements[i].name, sdef->elements[i].typedescrname);
-        }
-        else if (sdef->elements[i].isOptional) {
-          src = mputprintf(src,
-            "    field_%s = OMIT_VALUE;\n"
-            , sdef->elements[i].name);
-        } else {
-          src = mputprintf(src,
-            "    JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_MISSING_FIELD_ERROR, \"%s\");\n"
-            "    return JSON_ERROR_FATAL;\n"
-            , sdef->elements[i].dispname);
-        }
-        src = mputstr(src,
-          "  }\n");
-      }
-      src = mputstr(src,
-        "\n  return (int)dec_len;\n");
-    }
-    src = mputstr(src, "}\n\n");
+    src = generate_json_decoder(src, sdef);
   }
   
   if (oer_needed) {
@@ -6541,7 +6627,7 @@ static void defEmptyRecordClass(const struct_def *sdef,
     
     // JSON decode, RT1
     src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n"
+      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n"
       "{\n"
       "  if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n"
       // use the default value
@@ -7099,6 +7185,7 @@ void defRecordClass2(const struct_def *sdef, output_struct *output)
 
   boolean xer_needed = sdef->hasXer && enable_xer();
   boolean raw_needed = sdef->hasRaw && enable_raw();
+  boolean json_needed = sdef->hasJson && enable_json();
   boolean has_optional = FALSE;
   boolean has_default  = FALSE;
 
@@ -7485,6 +7572,21 @@ check_generate_end:
         "{ return FALSE; }\n"
         );
     } /* if (xer_needed) */
+    
+    if (json_needed) {
+      for (i = 0; i < sdef->nElements; ++i) {
+        if (sdef->elements[i].jsonChosen != NULL) {
+          /* if any of the fields has the 'chosen' attribute,
+             then the JSON decoder needs to be generated,
+             otherwise Record_Type::JSON_decode is enough */
+          def = mputprintf(def,
+            "int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, "
+            "boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);\n");
+          src = generate_json_decoder(src, sdef);
+          break;
+        }
+      }
+    }
   } /* if (sdef->nElements > 0) */
 
   /* end of class definition */
diff --git a/compiler2/record_of.c b/compiler2/record_of.c
index 32b7333a7..0a2a7cd8b 100644
--- a/compiler2/record_of.c
+++ b/compiler2/record_of.c
@@ -1567,7 +1567,7 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output)
     
     // JSON decode, RT1
     src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n"
+      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n"
       "{\n"
       "  if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n"
       // use the default value (currently only the empty array can be set as
@@ -3083,7 +3083,7 @@ void defRecordOfClassMemAllocOptimized(const struct_of_def *sdef, output_struct
     
     // JSON decode, RT1, mem. alloc. optimised
     src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)\n"
+      "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)\n"
       "{\n"
       "  json_token_t token = JSON_TOKEN_NONE;\n"
       "  size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\n"
diff --git a/compiler2/ttcn3/JsonAST.cc b/compiler2/ttcn3/JsonAST.cc
index e73f383e5..82d13e359 100644
--- a/compiler2/ttcn3/JsonAST.cc
+++ b/compiler2/ttcn3/JsonAST.cc
@@ -35,6 +35,7 @@ void JsonAST::init_JsonAST()
   default_value = NULL;
   metainfo_unbound = false;
   as_number = false;
+  tag_list = NULL;
 }
 
 JsonAST::JsonAST(const JsonAST *other_val)
@@ -61,6 +62,10 @@ JsonAST::~JsonAST()
     delete schema_extensions[i];
   }
   schema_extensions.clear();
+  if (tag_list != NULL) {
+    free_rawAST_tag_list(tag_list);
+    delete tag_list;
+  }
 }
 
 void JsonAST::print_JsonAST() const
@@ -92,4 +97,26 @@ void JsonAST::print_JsonAST() const
   if (metainfo_unbound) {
     printf("Metainfo for unbound field(s)\n\r");
   }
+  if (tag_list != NULL) {
+    printf("Chosen union fields:\n\r");
+    printf("  Number of rules: %d\n\r", tag_list->nElements);
+    for (int i = 0; i < tag_list->nElements; ++i) {
+      printf("  Rule #%d:\n\r", i);
+      printf("    Chosen field: %s\n\r", tag_list->tag[i].fieldName != NULL ?
+        tag_list->tag[i].fieldName->get_name().c_str() : "omit");
+      printf("    Number of conditions: %d\n\r", tag_list->tag[i].nElements);
+      for (int j = 0; j < tag_list->tag[i].nElements; ++j) {
+        printf("    Condition #%d:\n\r", j);
+        printf("      Value: %s\n\r", tag_list->tag[i].keyList[j].value);
+        printf("      Field: ");
+        for (int k = 0; k < tag_list->tag[i].keyList[j].keyField->nElements; ++k) {
+          if (k != 0) {
+            printf(".");
+          }
+          printf("%s", tag_list->tag[i].keyList[j].keyField->names[k]->get_name().c_str());
+        }
+        printf("\n\r");
+      }
+    }
+  }
 }
diff --git a/compiler2/ttcn3/JsonAST.hh b/compiler2/ttcn3/JsonAST.hh
index eea501822..5ae3cbfe5 100644
--- a/compiler2/ttcn3/JsonAST.hh
+++ b/compiler2/ttcn3/JsonAST.hh
@@ -15,6 +15,7 @@
 
 #include "../datatypes.h"
 #include "../vector.hh"
+#include "RawAST.hh"
 
 class JsonSchemaExtension {
 private:
@@ -41,6 +42,7 @@ class JsonAST {
     vector<JsonSchemaExtension> schema_extensions;
     boolean metainfo_unbound;
     boolean as_number;
+    rawAST_tag_list* tag_list;
   
     JsonAST() { init_JsonAST(); }
     JsonAST(const JsonAST *other_val);
diff --git a/compiler2/ttcn3/RawAST.hh b/compiler2/ttcn3/RawAST.hh
index 4a60952f1..d08ef58c6 100644
--- a/compiler2/ttcn3/RawAST.hh
+++ b/compiler2/ttcn3/RawAST.hh
@@ -51,9 +51,9 @@ typedef struct {
 } rawAST_tag_field_value;
 
 typedef struct {
-    Common::Identifier* fieldName;
+    Common::Identifier* fieldName; // NULL == omit
     int nElements;
-    rawAST_tag_field_value* keyList;
+    rawAST_tag_field_value* keyList; // NULL == otherwise/OTHERWISE
 } rawAST_single_tag;
 
 typedef struct {
diff --git a/compiler2/ttcn3/rawAST.l b/compiler2/ttcn3/rawAST.l
index 759281464..5ddba1c23 100644
--- a/compiler2/ttcn3/rawAST.l
+++ b/compiler2/ttcn3/rawAST.l
@@ -497,6 +497,8 @@ extend        { BEGIN(jsoncodec); RETURN(XKWextend); }
 metainfo      RETURN(XKWmetainfo);
 for           RETURN(XKWfor);
 unbound       RETURN(XKWunbound);
+chosen        RETURN(XChosenKeyword);
+otherwise     RETURN(XJsonOtherwise);
 }
 
 <INITIAL>{
diff --git a/compiler2/ttcn3/rawAST.y b/compiler2/ttcn3/rawAST.y
index 94baf56fc..bab7ef37a 100644
--- a/compiler2/ttcn3/rawAST.y
+++ b/compiler2/ttcn3/rawAST.y
@@ -294,6 +294,8 @@ static void yyprint(FILE *file, int type, const YYSTYPE& value);
 %token XJsonValueEnd      ")"
 %token XJsonValueSegment  "JSON value"
 %token XAsValueKeyword    "asValue"
+%token XChosenKeyword     "chosen"
+%token XJsonOtherwise     "otherwise"
 
 
 %type <enumval>
@@ -618,7 +620,9 @@ XAssocList:
     if ($3.nElements > 0) {
       /* the otherwise element is never merged */
       for (int i = 0; i < $1.nElements; i++)
-	if (*$1.tag[i].fieldName == *$3.fieldName) {
+	if (($1.tag[i].fieldName == NULL && $3.fieldName == NULL) ||
+	    ($1.tag[i].fieldName != NULL && $3.fieldName != NULL &&
+	     *$1.tag[i].fieldName == *$3.fieldName)) {
 	  dupl_id_index = i;
 	  break;
 	}
@@ -667,6 +671,30 @@ XAssocElement:
     $$.nElements = 0;
     $$.keyList = NULL;
   }
+| XIdentifier ',' XJsonOtherwise // JSON version
+  {
+    $$.fieldName = $1;
+    $$.nElements = 0;
+    $$.keyList = NULL;
+  }
+| XOmitKeyword ',' XKeyIdOrIdList
+  {
+    $$.fieldName = NULL;
+    $$.nElements = $3.nElements;
+    $$.keyList = $3.keyList;
+  }
+| XOmitKeyword ',' XOtherwise
+  {
+    $$.fieldName = NULL;
+    $$.nElements = 0;
+    $$.keyList = NULL;
+  }
+| XOmitKeyword ',' XJsonOtherwise // JSON version
+  {
+    $$.fieldName = NULL;
+    $$.nElements = 0;
+    $$.keyList = NULL;
+  }
 ;
 
 XKeyIdOrIdList:
@@ -1760,6 +1788,7 @@ JSONattribute:
 | JExtend
 | JMetainfoForUnbound
 | JAsNumber
+| JChosen
 ;
 
 JOmitAsNull:
@@ -1787,6 +1816,19 @@ JAsNumber:
   XKWas XKWnumber { jsonstruct->as_number = true; }
 ;
 
+JChosen:
+  XChosenKeyword '(' XAssocList XoptSemiColon ')'
+  {
+    if (jsonstruct->tag_list == NULL) {
+      jsonstruct->tag_list = new rawAST_tag_list;
+    }
+    else {
+      free_rawAST_tag_list(jsonstruct->tag_list);
+    }
+    link_rawAST_tag_list(jsonstruct->tag_list, &$3);
+  }
+;
+
 %%
 
 /* parse_rawAST(), which calls our rawAST_parse, is over in rawASST.l */
diff --git a/compiler2/ttcn3/rawASTspec.h b/compiler2/ttcn3/rawASTspec.h
index 73fdbaa49..7f2cab25c 100644
--- a/compiler2/ttcn3/rawASTspec.h
+++ b/compiler2/ttcn3/rawASTspec.h
@@ -58,7 +58,7 @@ typedef struct{
 
 typedef struct{
     const char* fieldName;
-    int fieldnum;
+    int fieldnum; /* -2 == omit (for JSON only) */
     int nElements;
     rawAST_coding_field_list* fields;
 }rawAST_coding_taglist;
diff --git a/compiler2/union.c b/compiler2/union.c
index 4023b4713..e4f0603e0 100644
--- a/compiler2/union.c
+++ b/compiler2/union.c
@@ -2178,11 +2178,26 @@ void defUnionClass(struct_def const *sdef, output_struct *output)
     
     // JSON decode
     src = mputprintf(src,
-      "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, boolean p_silent)\n"
+      "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, "
+      "boolean p_silent, int p_chosen_field)\n"
       "{\n"
       , name, sdef->nElements > 0 && !sdef->jsonAsValue ? " p_td" : "");
     if (sdef->nElements > 0) {
-      src = mputstr(src, "  json_token_t j_token = JSON_TOKEN_NONE;\n");
+      src = mputprintf(src,
+        "  if (0 <= p_chosen_field && %d > p_chosen_field) {\n"
+        "    switch (p_chosen_field) {\n"
+        ,(int)sdef->nElements);
+      for (i = 0; i < sdef->nElements; ++i) {
+        src = mputprintf(src,
+          "    case %d:\n"
+          "      return %s%s().JSON_decode(%s_descr_, p_tok, TRUE);\n"
+          , (int)i, at_field, sdef->elements[i].name
+          , sdef->elements[i].typedescrname);
+      }
+      src = mputstr(src, 
+        "    }\n"
+        "  }\n"
+        "  json_token_t j_token = JSON_TOKEN_NONE;\n");
       if (!sdef->jsonAsValue) {
         src = mputstr(src,
           " if (NULL != p_td.json && p_td.json->as_value) {\n");
diff --git a/core/ASN_Null.cc b/core/ASN_Null.cc
index 0c3bdccd5..02f27fd1c 100644
--- a/core/ASN_Null.cc
+++ b/core/ASN_Null.cc
@@ -318,7 +318,7 @@ int ASN_NULL::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) c
   return p_tok.put_next_token(JSON_TOKEN_LITERAL_NULL);
 }
 
-int ASN_NULL::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent)
+int ASN_NULL::JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);
diff --git a/core/ASN_Null.hh b/core/ASN_Null.hh
index 3f68f3180..19b42fbbc 100644
--- a/core/ASN_Null.hh
+++ b/core/ASN_Null.hh
@@ -93,7 +93,7 @@ public:
   
   /** Decodes accordingly to the JSON decoding rules.
     * Returns the length of the encoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Array.hh b/core/Array.hh
index 006d5d081..b56e6a2b2 100644
--- a/core/Array.hh
+++ b/core/Array.hh
@@ -967,7 +967,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   // alt-status priority: ALT_YES (return immediately) > ALT_REPEAT > ALT_MAYBE > ALT_NO
   alt_status done(VERDICTTYPE* value_redirect, Index_Redirect* index_redirect) const
@@ -1355,7 +1355,7 @@ int VALUE_ARRAY<T_type,array_size,index_offset>::JSON_encode(
 
 template <typename T_type, unsigned int array_size, int index_offset>
 int VALUE_ARRAY<T_type,array_size,index_offset>::JSON_decode(
-  const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+  const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);
diff --git a/core/Basetype.cc b/core/Basetype.cc
index 342983d25..921915b61 100644
--- a/core/Basetype.cc
+++ b/core/Basetype.cc
@@ -1026,7 +1026,7 @@ int Base_Type::JSON_encode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&) c
   return 0;
 }
 
-int Base_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean) 
+int Base_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean, int) 
 {
   TTCN_error("JSON decoding requested for type '%s' which has no"
              " JSON decoding method.", p_td.name);
diff --git a/core/Basetype.hh b/core/Basetype.hh
index cf22a7ef5..66cba68ba 100644
--- a/core/Basetype.hh
+++ b/core/Basetype.hh
@@ -25,6 +25,7 @@
 #define BASETYPE_HH
 
 #include "Types.h"
+#include "JSON.hh"
 #include "Encdec.hh"
 #include "RInt.hh"
 #include "JSON_Tokenizer.hh"
@@ -613,7 +614,7 @@ public:
   /** Decode JSON.
    * @return decoded length
    * @note Basetype::JSON_decode throws an error. */
-  VIRTUAL_IF_RUNTIME_2 int JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean);
+  VIRTUAL_IF_RUNTIME_2 int JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encode OER.
    * @return encoded length
@@ -865,7 +866,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
@@ -1028,7 +1029,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the JSON encoding rules.
     * Returns the length of the encoded data. */
@@ -1107,7 +1108,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  virtual int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  virtual int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Bitstring.cc b/core/Bitstring.cc
index adf323630..a9b615706 100644
--- a/core/Bitstring.cc
+++ b/core/Bitstring.cc
@@ -1209,7 +1209,7 @@ int BITSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok)
   return enc_len;
 }
 
-int BITSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int BITSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Bitstring.hh b/core/Bitstring.hh
index 6c5154703..afe3e89bb 100644
--- a/core/Bitstring.hh
+++ b/core/Bitstring.hh
@@ -208,7 +208,7 @@ public:
   
   /** Decodes accordingly to the JSON decoding rules.
     * Returns the length of the encoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Boolean.cc b/core/Boolean.cc
index 3572ae5f7..32b3aa596 100644
--- a/core/Boolean.cc
+++ b/core/Boolean.cc
@@ -758,7 +758,7 @@ int BOOLEAN::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) co
   return p_tok.put_next_token((boolean_value) ? JSON_TOKEN_LITERAL_TRUE : JSON_TOKEN_LITERAL_FALSE, NULL);
 }
 
-int BOOLEAN::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int BOOLEAN::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   size_t dec_len = 0;
diff --git a/core/Boolean.hh b/core/Boolean.hh
index d52476cfe..62df05246 100644
--- a/core/Boolean.hh
+++ b/core/Boolean.hh
@@ -129,7 +129,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Charstring.cc b/core/Charstring.cc
index 2a87aadb0..04246b9c4 100644
--- a/core/Charstring.cc
+++ b/core/Charstring.cc
@@ -1797,7 +1797,7 @@ int CHARSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok)
   return enc_len;
 }
 
-int CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Charstring.hh b/core/Charstring.hh
index 114b804aa..1a50d464c 100644
--- a/core/Charstring.hh
+++ b/core/Charstring.hh
@@ -278,7 +278,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   int OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const;
   
diff --git a/core/Float.cc b/core/Float.cc
index e790b0479..18bcb737f 100644
--- a/core/Float.cc
+++ b/core/Float.cc
@@ -1103,7 +1103,7 @@ int FLOAT::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) cons
   return enc_len;
 }
 
-int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   bound_flag = FALSE;
   json_token_t token = JSON_TOKEN_NONE;
diff --git a/core/Float.hh b/core/Float.hh
index 169946af7..73a17365e 100644
--- a/core/Float.hh
+++ b/core/Float.hh
@@ -160,7 +160,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Hexstring.cc b/core/Hexstring.cc
index d09f5ddbc..6b41443f7 100644
--- a/core/Hexstring.cc
+++ b/core/Hexstring.cc
@@ -1085,7 +1085,7 @@ int HEXSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok)
   return enc_len;
 }
 
-int HEXSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int HEXSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Hexstring.hh b/core/Hexstring.hh
index 5326d8bfb..1b414a8bb 100644
--- a/core/Hexstring.hh
+++ b/core/Hexstring.hh
@@ -161,7 +161,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
 };
 
 class HEXSTRING_ELEMENT {
diff --git a/core/Integer.cc b/core/Integer.cc
index 4cc53d6fb..1301fae04 100644
--- a/core/Integer.cc
+++ b/core/Integer.cc
@@ -1741,7 +1741,7 @@ int INTEGER::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) co
   return enc_len;
 }
 
-int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Integer.hh b/core/Integer.hh
index a538db16f..32f68f11b 100644
--- a/core/Integer.hh
+++ b/core/Integer.hh
@@ -191,7 +191,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/JSON.hh b/core/JSON.hh
index 050df3220..9b8b74757 100644
--- a/core/JSON.hh
+++ b/core/JSON.hh
@@ -125,6 +125,11 @@ enum json_metainfo_t {
   JSON_METAINFO_UNBOUND
 };
 
+enum json_chosen_field_t {
+  CHOSEN_FIELD_UNSET = -1,
+  CHOSEN_FIELD_OMITTED = -2
+};
+
 // JSON decoding error messages
 #define JSON_DEC_BAD_TOKEN_ERROR "Failed to extract valid token, invalid JSON format%s"
 #define JSON_DEC_FORMAT_ERROR "Invalid JSON %s format, expecting %s value"
@@ -141,6 +146,9 @@ enum json_metainfo_t {
 #define JSON_DEC_METAINFO_NAME_ERROR "Meta info provided for non-existent field '%.*s'"
 #define JSON_DEC_METAINFO_VALUE_ERROR "Invalid meta info for field '%s'"
 #define JSON_DEC_METAINFO_NOT_APPLICABLE "Meta info not applicable to field '%s'"
+#define JSON_DEC_CHOSEN_FIELD_NOT_NULL "Invalid JSON token, expecting 'null' (as indicated by a condition in attribute 'chosen')%s"
+#define JSON_DEC_CHOSEN_FIELD_OMITTED "Field '%s' cannot be omitted (as indicated by a condition in attribute 'chosen')"
+#define JSON_DEC_CHOSEN_FIELD_OMITTED_NULL "Field cannot be omitted (as indicated by a condition in attribute 'chosen')%s"
 
 
 // Functions for conversion between json and cbor and vice versa
diff --git a/core/Objid.cc b/core/Objid.cc
index 6e125186f..1a4944706 100644
--- a/core/Objid.cc
+++ b/core/Objid.cc
@@ -631,7 +631,7 @@ int OBJID::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok) cons
   return enc_len;
 }
 
-int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Objid.hh b/core/Objid.hh
index db7901d78..12735b6a0 100644
--- a/core/Objid.hh
+++ b/core/Objid.hh
@@ -114,7 +114,7 @@ public:
   
   /** Decodes accordingly to the JSON decoding rules.
     * Returns the length of the encoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Octetstring.cc b/core/Octetstring.cc
index bb50c2239..b4da029c0 100644
--- a/core/Octetstring.cc
+++ b/core/Octetstring.cc
@@ -1331,7 +1331,7 @@ int OCTETSTRING::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok
   return enc_len;
 }
 
-int OCTETSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int OCTETSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Octetstring.hh b/core/Octetstring.hh
index 6ecc3089b..d261be4f4 100644
--- a/core/Octetstring.hh
+++ b/core/Octetstring.hh
@@ -192,7 +192,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
diff --git a/core/Optional.hh b/core/Optional.hh
index cbddc0bca..c914e79b1 100644
--- a/core/Optional.hh
+++ b/core/Optional.hh
@@ -340,7 +340,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   /** Encodes accordingly to the OER encoding rules.
     * Returns the length of the encoded data. */
@@ -871,12 +871,28 @@ int OPTIONAL<T_type>::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_de
 #endif
 
 template<typename T_type>
-int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int p_chosen_field)
 {
   // try the optional value first
   set_to_present();
   size_t buf_pos = p_tok.get_buf_pos();
-  int dec_len = optional_value->JSON_decode(p_td, p_tok, p_silent);
+  int dec_len = 0;
+  if (CHOSEN_FIELD_OMITTED == p_chosen_field) {
+    // the attribute 'chosen' says that this field has to be omitted
+    json_token_t token = JSON_TOKEN_NONE;
+    dec_len = p_tok.get_next_token(&token, NULL, NULL);
+    if (JSON_TOKEN_LITERAL_NULL == token) {
+      set_to_omit();
+      return dec_len;
+    }
+    else {
+      JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_NOT_NULL, "");
+      // if this is set to warning, return to the beginning of the value and
+      // decode it as normal
+      p_tok.set_buf_pos(buf_pos);
+    }
+  }
+  dec_len = optional_value->JSON_decode(p_td, p_tok, p_silent, p_chosen_field);
   if (JSON_ERROR_FATAL == dec_len) {
     if (p_silent) {
       clean_up();
@@ -892,6 +908,9 @@ int OPTIONAL<T_type>::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokeni
     json_token_t token = JSON_TOKEN_NONE;
     dec_len = p_tok.get_next_token(&token, NULL, NULL);
     if (JSON_TOKEN_LITERAL_NULL == token) {
+      if (0 <= p_chosen_field) {
+        JSON_ERROR(TTCN_EncDec::ET_INVAL_MSG, JSON_DEC_CHOSEN_FIELD_OMITTED_NULL, "");
+      }
       set_to_omit();
     }
     else {
diff --git a/core/TTCN3.hh b/core/TTCN3.hh
index 4f40830f7..e85261a2e 100644
--- a/core/TTCN3.hh
+++ b/core/TTCN3.hh
@@ -41,6 +41,7 @@
 #include "version.h"
 #include <cversion.h>
 
+#include "JSON.hh"
 #include "Vector.hh"
 #include "Basetype.hh"
 #include "Template.hh"
@@ -87,7 +88,6 @@
 #include "RAW.hh"
 #include "TEXT.hh"
 #include "XER.hh"
-#include "JSON.hh"
 #include "OER.hh"
 #include "Error.hh"
 #include "XmlReader.hh"
diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc
index ea0cc0307..1cc00d377 100644
--- a/core/Universal_charstring.cc
+++ b/core/Universal_charstring.cc
@@ -2584,7 +2584,7 @@ int UNIVERSAL_CHARSTRING::JSON_encode(const TTCN_Typedescriptor_t& /*p_td*/, JSO
   return enc_len;
 }
 
-int UNIVERSAL_CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int UNIVERSAL_CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Universal_charstring.hh b/core/Universal_charstring.hh
index 3888721fd..0249e4a0f 100644
--- a/core/Universal_charstring.hh
+++ b/core/Universal_charstring.hh
@@ -403,7 +403,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
   
   int OER_encode(const TTCN_Typedescriptor_t&, TTCN_Buffer&) const;
   
diff --git a/core/Verdicttype.cc b/core/Verdicttype.cc
index d87c25618..7091f4288 100644
--- a/core/Verdicttype.cc
+++ b/core/Verdicttype.cc
@@ -382,7 +382,7 @@ int VERDICTTYPE::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& p_tok
   return enc_len;
 }
 
-int VERDICTTYPE::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int VERDICTTYPE::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   json_token_t token = JSON_TOKEN_NONE;
   char* value = 0;
diff --git a/core/Verdicttype.hh b/core/Verdicttype.hh
index 097df87cb..719779215 100644
--- a/core/Verdicttype.hh
+++ b/core/Verdicttype.hh
@@ -108,7 +108,7 @@ public:
   
   /** Decodes accordingly to the JSON encoding rules.
     * Returns the length of the decoded data. */
-  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean);
+  int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean, int p_chosen_field = CHOSEN_FIELD_UNSET);
 };
 
 inline boolean operator==(verdicttype par_value, verdicttype other_value)
diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc
index f9e669021..00132ca1e 100644
--- a/core2/Basetype2.cc
+++ b/core2/Basetype2.cc
@@ -1587,7 +1587,7 @@ int Record_Of_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_desc
   return enc_len;
 }
 
-int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {
     // use the default value (currently only the empty array can be set as
@@ -6081,7 +6081,7 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr,
   return enc_len;
 }
 
-int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   if (NULL != p_td.json && p_td.json->as_value) {
     // if 'as value' is set, then the record/set has only one field,
@@ -7312,7 +7312,7 @@ int Empty_Record_Type::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&
     p_tok.put_next_token(JSON_TOKEN_OBJECT_END, NULL);
 }
 
-int Empty_Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent)
+int Empty_Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, int)
 {
   if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {
     // use the default value
diff --git a/function_test/Semantic_Analyser/Makefile.semantic b/function_test/Semantic_Analyser/Makefile.semantic
index 5f3a543eb..a573d2527 100644
--- a/function_test/Semantic_Analyser/Makefile.semantic
+++ b/function_test/Semantic_Analyser/Makefile.semantic
@@ -15,7 +15,8 @@
 include ../../Makefile.personal
 
 SADIRS := ver param template any_from pattern_ref float recof_index \
-port_translation mtc_and_system_clause port_map_connect deterministic
+port_translation mtc_and_system_clause port_map_connect deterministic \
+json
 ifdef RT2
 SADIRS += deprecated erroneous_attributes template_concat
 endif
diff --git a/function_test/Semantic_Analyser/json/.gitignore b/function_test/Semantic_Analyser/json/.gitignore
new file mode 100644
index 000000000..e2d293255
--- /dev/null
+++ b/function_test/Semantic_Analyser/json/.gitignore
@@ -0,0 +1,2 @@
+!Makefile
+!*.ttcn
diff --git a/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn b/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn
new file mode 100644
index 000000000..0812d87a5
--- /dev/null
+++ b/function_test/Semantic_Analyser/json/JsonChosen_SE.ttcn
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * Copyright (c) 2000-2017 Ericsson Telecom AB
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *   Baranyi, Botond
+ *
+ ******************************************************************************/
+module JsonChosen_SE { //^In TTCN-3 module//
+
+type union Uni {
+  integer f1,
+  charstring f2,
+  octetstring f3
+}
+
+type record Rec { //^In type definition//
+  integer num,
+  charstring str,
+  octetstring field1, //Invalid attribute, 'chosen' is only allowed for fields of records and sets of union type//
+  Uni field2, //Reference to invalid union field name `nonexistent' for type `@JsonChosen_SE.Uni', in JSON attribute 'chosen'// //^In JSON attribute 'choice'// 
+  Uni field3, //Target of JSON attribute 'chosen' is a mandatory field and cannot be set to 'omit'//
+  Uni field4 optional //Too many field references in JSON attribute 'chosen'. Type `integer' doesn't have fields// //Reference to invalid field name `nonexistent' for type `@JsonChosen_SE.Uni', in JSON attribute 'chosen'//
+}
+with {
+  variant (field1) "chosen (nonexistent, num = 1)";
+  variant (field2) "chosen (nonexistent, num = 1; f1, num = 100; f2, { str = 1, str = \"a\" })"; //character string value was expected//
+  variant (field3) "chosen (omit, num = 1)";
+  variant (field4) "chosen (f1, num = 4; f2, field2.f1.toomany = 22; f3, field3.nonexistent = 1111)";
+}
+  
+}
+with {
+  encode "JSON";
+}
diff --git a/function_test/Semantic_Analyser/json/Makefile b/function_test/Semantic_Analyser/json/Makefile
new file mode 100644
index 000000000..3f369041a
--- /dev/null
+++ b/function_test/Semantic_Analyser/json/Makefile
@@ -0,0 +1,12 @@
+##############################################################################
+# Copyright (c) 2000-2017 Ericsson Telecom AB
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Eclipse Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Contributors:
+#   Baranyi, Botond
+#
+##############################################################################
+include ../common.mk
diff --git a/function_test/Semantic_Analyser/json/t b/function_test/Semantic_Analyser/json/t
new file mode 100755
index 000000000..3a4b58ec1
--- /dev/null
+++ b/function_test/Semantic_Analyser/json/t
@@ -0,0 +1,9 @@
+#!/usr/bin/perl
+# note this is called through "perl -w"
+use strict;
+
+my $self = $0;
+$self =~ s!/t!!;
+
+exec('make check --no-print-directory -s -C ' . $self);
+
diff --git a/regression_test/json/AttributeTestcases.ttcn b/regression_test/json/AttributeTestcases.ttcn
index b034d3598..1b6141fff 100644
--- a/regression_test/json/AttributeTestcases.ttcn
+++ b/regression_test/json/AttributeTestcases.ttcn
@@ -521,6 +521,112 @@ testcase tc_attribute_default_struct() runs on MTC {
   }
 }
 
+// Testing the attribute 'chosen'
+testcase tc_attribute_chosen() runs on MTC {
+  // one test for each chosen field
+  var PduWithIdList val := {
+    { protocolId := 11, field1 := { type1 := { num := 1, str := "abc" } }, field2 := { type2 := { num := 2, str := "xyz" } } },
+    { protocolId := 2, field1 := { type2 := { num := 1, str := "abc" } }, field2 := { type1 := { num := 2, str := "xyz" } } },
+    { protocolId := 3, field1 := omit, field2 := { type3 := { num := 2.0, str := "xyz" } } },
+    { protocolId := 65536, field1 := { type3 := { num := 1.0, str := "abc" } }, field2 := omit }
+  };
+  var universal charstring enc_exp := "[" &
+    "{\"protocolId\":11,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}," &
+    "{\"protocolId\":2,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}," &
+    "{\"protocolId\":3,\"field2\":{\"num\":2.000000,\"str\":\"xyz\"}}," &
+    "{\"protocolId\":65536,\"field1\":{\"num\":1.000000,\"str\":\"abc\"}}" &
+  "]";
+  var universal charstring enc := encvalue_unichar(val);
+  if (enc != enc_exp) {
+    setverdict(fail, "Encoding failed. Expected: ", enc_exp, ", got: ", enc);
+  }
+  var PduWithIdList dec;
+  var integer res := decvalue_unichar(enc_exp, dec);
+  if (res != 0) {
+    setverdict(fail, "Decoding #1 failed. Result: ", res);
+  }
+  else if (dec != val) {
+    setverdict(fail, "Decoding #1 failed. Decoded value: ", dec, ", expected: ", val);
+  }
+  
+  // these should also be correctly decoded to the initial values
+  var universal charstring buff := "[" &
+    "{\"protocolId\":65536,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":null}," &
+    "{\"protocolId\":3,\"field1\":null,\"field2\":{\"num\":2,\"str\":\"xyz\"}}" &
+  "]";
+  var PduWithIdList dec2;
+  var PduWithIdList dec2_exp := { val[3], val[2] };
+  res := decvalue_unichar(buff, dec2);
+  if (res != 0) {
+    setverdict(fail, "Decoding #2 failed. Result: ", res);
+  }
+  else if (dec2 != dec2_exp) {
+    setverdict(fail, "Decoding #2 failed. Decoded value: ", dec2, ", expected: ", dec2_exp);
+  }
+  setverdict(pass);
+}
+
+// Testing JSON decoding when field selections of optional fields conflict with
+// the settings in attribute 'chosen'
+testcase tc_attribute_chosen_negtest() runs on MTC {
+  // Test #1: protocolId indicates 'omit' (for field1), but the field is not omitted
+  @try {
+    var octetstring buff := char2oct("{\"protocolId\":3,\"field1\":{\"num\":1,\"str\":\"abc\"},\"field2\":{\"num\":2,\"str\":\"xyz\"}}");
+    var PduWithId dec := f_dec_pduwithid(buff);
+    setverdict(fail, "Test #1. Expected decoding failure.");
+  }
+  @catch (msg) {
+    var template charstring tmp :=
+      pattern "*Invalid JSON token, expecting 'null' \(as indicated by a condition in attribute 'chosen'\)";
+    if (not match(msg, tmp)) {
+      setverdict(fail, "Test #1. Invalid error message received: ", msg);
+    }
+  }
+  
+  // Test #2: protocolId indicates a union field (type1, for field1), but the field is omitted (with the JSON value 'null')
+  @try {
+    var octetstring buff := char2oct("{\"protocolId\":1,\"field1\":null}");
+    var PduWithId dec := f_dec_pduwithid(buff);
+    setverdict(fail, "Test #2. Expected decoding failure.");
+  }
+  @catch (msg) {
+    var template charstring tmp :=
+      pattern "*Field cannot be omitted \(as indicated by a condition in attribute 'chosen'\)";
+    if (not match(msg, tmp)) {
+      setverdict(fail, "Test #2. Invalid error message received: ", msg);
+    }
+  }
+  
+  // Test #3: protocolId indicates a union field (type1, for field1), but the field is omitted (with no JSON name-value pair)
+  @try {
+    var octetstring buff := char2oct("{\"protocolId\":1}");
+    var PduWithId dec := f_dec_pduwithid(buff);
+    setverdict(fail, "Test #3. Expected decoding failure.");
+  }
+  @catch (msg) {
+    var template charstring tmp :=
+      pattern "*Field 'field1' cannot be omitted \(as indicated by a condition in attribute 'chosen'\)";
+    if (not match(msg, tmp)) {
+      setverdict(fail, "Test #3. Invalid error message received: ", msg);
+    }
+  }
+  
+  // Test #4: protocolId indicates a union field (type3, for field2), but the field is omitted (with no JSON name-value pair)
+  @try {
+    var octetstring buff := char2oct("{\"protocolId\":3}");
+    var PduWithId dec := f_dec_pduwithid(buff);
+    setverdict(fail, "Test #3. Expected decoding failure.");
+  }
+  @catch (msg) {
+    var template charstring tmp :=
+      pattern "*Field 'field2' cannot be omitted \(as indicated by a condition in attribute 'chosen'\)";
+    if (not match(msg, tmp)) {
+      setverdict(fail, "Test #3. Invalid error message received: ", msg);
+    }
+  }
+  setverdict(pass);
+}
+
 
 control {
   execute(tc_NoAttributeOnUpperLevel())
@@ -547,5 +653,7 @@ control {
   execute(tc_attribute_as_number());
   execute(tc_attribute_as_number_negtest());
   execute(tc_attribute_default_struct());
+  execute(tc_attribute_chosen());
+  execute(tc_attribute_chosen_negtest());
 }
 }
diff --git a/regression_test/json/Functions.ttcn b/regression_test/json/Functions.ttcn
index 9bf007dd4..edb639f08 100644
--- a/regression_test/json/Functions.ttcn
+++ b/regression_test/json/Functions.ttcn
@@ -426,6 +426,9 @@ external function f_dec_obj(in octetstring x) return Object
 
 external function f_dec_null(in octetstring x) return HasNull
   with { extension "prototype(convert) decode(JSON)" }
+
+external function f_dec_pduwithid(in octetstring x) return PduWithId
+  with { extension "prototype(convert) decode(JSON)" }
   
 //============== Internal Functions ====================
   
diff --git a/regression_test/json/Types.ttcn b/regression_test/json/Types.ttcn
index e802689d2..5ce1cd3d6 100644
--- a/regression_test/json/Types.ttcn
+++ b/regression_test/json/Types.ttcn
@@ -318,6 +318,39 @@ with {
   variant (so) "JSON: default({})";
 }
 
+type record PduWithId {
+  integer protocolId,
+  Choices field1 optional,
+  Choices field2 optional
+}
+with {
+  variant (field1) "chosen (type1, { protocolId = 1, protocolId = 11 }; type2, protocolId = 2; omit, protocolId = 3; type3, otherwise)";
+  variant (field2) "chosen (type1, protocolId = 2; type2, { protocolId = 10, protocolId = 11 }; type3, protocolId = 3)"; // no otherwise
+}
+
+type union Choices {
+  StructType1 type1,
+  StructType2 type2,
+  StructType3 type3
+}
+
+type record StructType1 {
+  integer num,
+  charstring str
+}
+
+type record StructType2 {
+  integer num,
+  universal charstring str
+}
+
+type record StructType3 {
+  float num,
+  charstring str
+}
+
+type record of PduWithId PduWithIdList;
+
 } with {
   encode "JSON";
   extension "anytype integer, charstring, R, RoI, Thing";
-- 
GitLab