DECODE_JSON(Variant) {
    ALLOW_NULL;
    CHECK_OBJECT;

    /* First search for the variant type in the json object. */
    size_t searchResultType = 0;
    status ret = lookAheadForKey(UA_JSONKEY_TYPE, ctx, parseCtx, &searchResultType);
    if(ret != UA_STATUSCODE_GOOD) {
        skipObject(parseCtx);
        return UA_STATUSCODE_GOOD;
    }

    size_t size = ((size_t)parseCtx->tokenArray[searchResultType].end -
                   (size_t)parseCtx->tokenArray[searchResultType].start);

    /* check if size is zero or the type is not a number */
    if(size < 1 || parseCtx->tokenArray[searchResultType].type != JSMN_PRIMITIVE)
        return UA_STATUSCODE_BADDECODINGERROR;
    
    /* Parse the type */
    UA_UInt64 idTypeDecoded = 0;
    char *idTypeEncoded = (char*)(ctx->pos + parseCtx->tokenArray[searchResultType].start);
    status typeDecodeStatus = atoiUnsigned(idTypeEncoded, size, &idTypeDecoded);
    if(typeDecodeStatus != UA_STATUSCODE_GOOD)
        return typeDecodeStatus;

    /* A NULL Variant */
    if(idTypeDecoded == 0) {
        skipObject(parseCtx);
        return UA_STATUSCODE_GOOD;
    }

    /* Set the type */
    UA_NodeId typeNodeId = UA_NODEID_NUMERIC(0, (UA_UInt32)idTypeDecoded);
    dst->type = UA_findDataType(&typeNodeId);
    if(!dst->type)
        return UA_STATUSCODE_BADDECODINGERROR;
    
    /* Search for body */
    size_t searchResultBody = 0;
    ret = lookAheadForKey(UA_JSONKEY_BODY, ctx, parseCtx, &searchResultBody);
    if(ret != UA_STATUSCODE_GOOD) {
        /*TODO: no body? set value NULL?*/
        return UA_STATUSCODE_BADDECODINGERROR;
    }

    /* value is an array? */
    UA_Boolean isArray = false;
    if(parseCtx->tokenArray[searchResultBody].type == JSMN_ARRAY) {
        isArray = true;
        dst->arrayLength = (size_t)parseCtx->tokenArray[searchResultBody].size;
    }

    /* Has the variant dimension? */
    UA_Boolean hasDimension = false;
    size_t searchResultDim = 0;
    ret = lookAheadForKey(UA_JSONKEY_DIMENSION, ctx, parseCtx, &searchResultDim);
    if(ret == UA_STATUSCODE_GOOD) {
        hasDimension = true;
        dst->arrayDimensionsSize = (size_t)parseCtx->tokenArray[searchResultDim].size;
    }
    
    /* no array but has dimension. error? */
    if(!isArray && hasDimension)
        return UA_STATUSCODE_BADDECODINGERROR;
    
    /* Get the datatype of the content. The type must be a builtin data type.
     * All not-builtin types are wrapped in an ExtensionObject. */
    if(dst->type->typeKind > UA_TYPES_DIAGNOSTICINFO)
        return UA_STATUSCODE_BADDECODINGERROR;

    /* A variant cannot contain a variant. But it can contain an array of
     * variants */
    if(dst->type->typeKind == UA_DATATYPEKIND_VARIANT && !isArray)
        return UA_STATUSCODE_BADDECODINGERROR;
    
    /* Decode an array */
    if(isArray) {
        DecodeEntry entries[3] = {
            {UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
            {UA_JSONKEY_BODY, &dst->data, (decodeJsonSignature) Array_decodeJson, false, NULL},
            {UA_JSONKEY_DIMENSION, &dst->arrayDimensions,
             (decodeJsonSignature) VariantDimension_decodeJson, false, NULL}};
        if(!hasDimension) {
            ret = decodeFields(ctx, parseCtx, entries, 2, dst->type); /*use first 2 fields*/
        } else {
            ret = decodeFields(ctx, parseCtx, entries, 3, dst->type); /*use all fields*/
        }      
        return ret;
    }

    /* Decode a value wrapped in an ExtensionObject */
    if(dst->type->typeKind == UA_DATATYPEKIND_EXTENSIONOBJECT) {
        DecodeEntry entries[2] =
            {{UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
             {UA_JSONKEY_BODY, dst,
              (decodeJsonSignature)Variant_decodeJsonUnwrapExtensionObject, false, NULL}};
        return decodeFields(ctx, parseCtx, entries, 2, dst->type);
    }

    /* Allocate Memory for Body */
    dst->data = UA_new(dst->type);
    if(!dst->data)
        return UA_STATUSCODE_BADOUTOFMEMORY;
    DecodeEntry entries[2] =
        {{UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
         {UA_JSONKEY_BODY, dst->data, (decodeJsonSignature) decodeJsonInternal, false, NULL}};
    return decodeFields(ctx, parseCtx, entries, 2, dst->type);
}
DECODE_JSON(Variant) {
    ALLOW_NULL;
    CHECK_OBJECT;

     
    size_t searchResultType = 0;
    status ret = lookAheadForKey(UA_JSONKEY_TYPE, ctx, parseCtx, &searchResultType);
    if(ret != UA_STATUSCODE_GOOD) {
        skipObject(parseCtx);
        return UA_STATUSCODE_GOOD;
    }

    size_t size = ((size_t)parseCtx->tokenArray[searchResultType].end -
                   (size_t)parseCtx->tokenArray[searchResultType].start);

     
    if(size < 1 || parseCtx->tokenArray[searchResultType].type != JSMN_PRIMITIVE)
        return UA_STATUSCODE_BADDECODINGERROR;
    
     
    UA_UInt64 idTypeDecoded = 0;
    char *idTypeEncoded = (char*)(ctx->pos + parseCtx->tokenArray[searchResultType].start);
    status typeDecodeStatus = atoiUnsigned(idTypeEncoded, size, &idTypeDecoded);
    if(typeDecodeStatus != UA_STATUSCODE_GOOD)
        return typeDecodeStatus;

     
    if(idTypeDecoded == 0) {
        skipObject(parseCtx);
        return UA_STATUSCODE_GOOD;
    }

     
    UA_NodeId typeNodeId = UA_NODEID_NUMERIC(0, (UA_UInt32)idTypeDecoded);
    dst->type = UA_findDataType(&typeNodeId);
    if(!dst->type)
        return UA_STATUSCODE_BADDECODINGERROR;
    
     
    size_t searchResultBody = 0;
    ret = lookAheadForKey(UA_JSONKEY_BODY, ctx, parseCtx, &searchResultBody);
    if(ret != UA_STATUSCODE_GOOD) {
         
        return UA_STATUSCODE_BADDECODINGERROR;
    }

     
    UA_Boolean isArray = false;
    if(parseCtx->tokenArray[searchResultBody].type == JSMN_ARRAY) {
        isArray = true;
        dst->arrayLength = (size_t)parseCtx->tokenArray[searchResultBody].size;
    }

     
    UA_Boolean hasDimension = false;
    size_t searchResultDim = 0;
    ret = lookAheadForKey(UA_JSONKEY_DIMENSION, ctx, parseCtx, &searchResultDim);
    if(ret == UA_STATUSCODE_GOOD) {
        hasDimension = true;
        dst->arrayDimensionsSize = (size_t)parseCtx->tokenArray[searchResultDim].size;
    }
    
     
    if(!isArray && hasDimension)
        return UA_STATUSCODE_BADDECODINGERROR;
    
     
    if(dst->type->typeKind > UA_TYPES_DIAGNOSTICINFO)
        return UA_STATUSCODE_BADDECODINGERROR;

     
    if(dst->type->typeKind == UA_DATATYPEKIND_VARIANT && !isArray)
        return UA_STATUSCODE_BADDECODINGERROR;
    
     
    if(isArray) {
        DecodeEntry entries[3] = {
            {UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
            {UA_JSONKEY_BODY, &dst->data, (decodeJsonSignature) Array_decodeJson, false, NULL},
            {UA_JSONKEY_DIMENSION, &dst->arrayDimensions,
             (decodeJsonSignature) VariantDimension_decodeJson, false, NULL}};
        if(!hasDimension) {
            ret = decodeFields(ctx, parseCtx, entries, 2, dst->type);  
        } else {
            ret = decodeFields(ctx, parseCtx, entries, 3, dst->type);  
        }      
        return ret;
    }

     
    if(dst->type->typeKind == UA_DATATYPEKIND_EXTENSIONOBJECT) {
        DecodeEntry entries[2] =
            {{UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
             {UA_JSONKEY_BODY, dst,
              (decodeJsonSignature)Variant_decodeJsonUnwrapExtensionObject, false, NULL}};
        return decodeFields(ctx, parseCtx, entries, 2, dst->type);
    }

     
    dst->data = UA_new(dst->type);
    if(!dst->data)
        return UA_STATUSCODE_BADOUTOFMEMORY;
    DecodeEntry entries[2] =
        {{UA_JSONKEY_TYPE, NULL, NULL, false, NULL},
         {UA_JSONKEY_BODY, dst->data, (decodeJsonSignature) decodeJsonInternal, false, NULL}};
    return decodeFields(ctx, parseCtx, entries, 2, dst->type);
}