ENCODE_JSON(ExtensionObject) {
    u8 encoding = (u8) src->encoding;
    if(encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY)
        return writeJsonNull(ctx);
    
    status ret = UA_STATUSCODE_GOOD;
    /* already encoded content.*/
    if(encoding <= UA_EXTENSIONOBJECT_ENCODED_XML) {
        ret |= writeJsonObjStart(ctx);
        if(ctx->useReversible) {
            ret |= writeJsonKey(ctx, UA_JSONKEY_TYPEID);
            ret |= ENCODE_DIRECT_JSON(&src->content.encoded.typeId, NodeId);
            if(ret != UA_STATUSCODE_GOOD)
                return ret;
        }
        
        switch (src->encoding) {
            case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
            {
                if(ctx->useReversible) {
                    ret |= writeJsonKey(ctx, UA_JSONKEY_ENCODING);
                    ret |= writeChar(ctx, '1');
                }
                ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
                ret |= ENCODE_DIRECT_JSON(&src->content.encoded.body, String);
                break;
            }
            case UA_EXTENSIONOBJECT_ENCODED_XML:
            {
                if(ctx->useReversible) {
                    ret |= writeJsonKey(ctx, UA_JSONKEY_ENCODING);
                    ret |= writeChar(ctx, '2');
                }
                ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
                ret |= ENCODE_DIRECT_JSON(&src->content.encoded.body, String);
                break;
            }
            default:
                ret = UA_STATUSCODE_BADINTERNALERROR;
        }

        ret |= writeJsonObjEnd(ctx);
        return ret;
    } /* encoding <= UA_EXTENSIONOBJECT_ENCODED_XML */
         
    /* Cannot encode with no type description */
    if(!src->content.decoded.type)
        return UA_STATUSCODE_BADENCODINGERROR;

    if(!src->content.decoded.data)
        return writeJsonNull(ctx);

    UA_NodeId typeId = src->content.decoded.type->typeId;
    if(typeId.identifierType != UA_NODEIDTYPE_NUMERIC)
        return UA_STATUSCODE_BADENCODINGERROR;

    ret |= writeJsonObjStart(ctx);
    const UA_DataType *contentType = src->content.decoded.type;
    if(ctx->useReversible) {
        /* REVERSIBLE */
        ret |= writeJsonKey(ctx, UA_JSONKEY_TYPEID);
        ret |= ENCODE_DIRECT_JSON(&typeId, NodeId);

        /* Encode the content */
        ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
        ret |= encodeJsonInternal(src->content.decoded.data, contentType, ctx);
    } else {
        /* NON-REVERSIBLE
         * For the non-reversible form, ExtensionObject values 
         * shall be encoded as a JSON object containing only the 
         * value of the Body field. The TypeId and Encoding fields are dropped.
         * 
         * TODO: UA_JSONKEY_BODY key in the ExtensionObject?
         */
        ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
        ret |= encodeJsonInternal(src->content.decoded.data, contentType, ctx);
    }

    ret |= writeJsonObjEnd(ctx);
    return ret;
}
ENCODE_JSON(ExtensionObject) {
    u8 encoding = (u8) src->encoding;
    if(encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY)
        return writeJsonNull(ctx);
    
    status ret = UA_STATUSCODE_GOOD;
     
    if(encoding <= UA_EXTENSIONOBJECT_ENCODED_XML) {
        ret |= writeJsonObjStart(ctx);
        if(ctx->useReversible) {
            ret |= writeJsonKey(ctx, UA_JSONKEY_TYPEID);
            ret |= ENCODE_DIRECT_JSON(&src->content.encoded.typeId, NodeId);
            if(ret != UA_STATUSCODE_GOOD)
                return ret;
        }
        
        switch (src->encoding) {
            case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
            {
                if(ctx->useReversible) {
                    ret |= writeJsonKey(ctx, UA_JSONKEY_ENCODING);
                    ret |= writeChar(ctx, '1');
                }
                ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
                ret |= ENCODE_DIRECT_JSON(&src->content.encoded.body, String);
                break;
            }
            case UA_EXTENSIONOBJECT_ENCODED_XML:
            {
                if(ctx->useReversible) {
                    ret |= writeJsonKey(ctx, UA_JSONKEY_ENCODING);
                    ret |= writeChar(ctx, '2');
                }
                ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
                ret |= ENCODE_DIRECT_JSON(&src->content.encoded.body, String);
                break;
            }
            default:
                ret = UA_STATUSCODE_BADINTERNALERROR;
        }

        ret |= writeJsonObjEnd(ctx);
        return ret;
    }  
         
     
    if(!src->content.decoded.type)
        return UA_STATUSCODE_BADENCODINGERROR;

    if(!src->content.decoded.data)
        return writeJsonNull(ctx);

    UA_NodeId typeId = src->content.decoded.type->typeId;
    if(typeId.identifierType != UA_NODEIDTYPE_NUMERIC)
        return UA_STATUSCODE_BADENCODINGERROR;

    ret |= writeJsonObjStart(ctx);
    const UA_DataType *contentType = src->content.decoded.type;
    if(ctx->useReversible) {
         
        ret |= writeJsonKey(ctx, UA_JSONKEY_TYPEID);
        ret |= ENCODE_DIRECT_JSON(&typeId, NodeId);

         
        ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
        ret |= encodeJsonInternal(src->content.decoded.data, contentType, ctx);
    } else {
         
        ret |= writeJsonKey(ctx, UA_JSONKEY_BODY);
        ret |= encodeJsonInternal(src->content.decoded.data, contentType, ctx);
    }

    ret |= writeJsonObjEnd(ctx);
    return ret;
}