diff --git a/src/ua_types_encoding_xml.c b/src/ua_types_encoding_xml.c
index 999ac3dbb..b668efd9c 100644
--- a/src/ua_types_encoding_xml.c
+++ b/src/ua_types_encoding_xml.c
@@ -109,7 +109,17 @@ XmlEncTypeDef xmlEncTypeDefs[UA_DATATYPEKINDS] = {
""
"", 330}, /* ExtensionObject */
{"", 0}, /* DataValue */
- {"", 0}, /* Variant */
+ {""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "", 248}, /* Variant */
{"", 0}, /* DiagnosticInfo */
{"", 0}, /* Decimal */
{"", 0}, /* Enum */
@@ -146,6 +156,43 @@ static const char* UA_XML_EXTENSIONOBJECT_TYPEID = "TypeId"; // NodeId
static const char* UA_XML_EXTENSIONOBJECT_BODY = "Body";
static const char* UA_XML_EXTENSIONOBJECT_BYTESTRING = "ByteString";
+/* Variant */
+static const char* UA_XML_VARIANT_VALUE = "Value";
+
+const char* xsdTypeNames[UA_DATATYPEKINDS] = {
+ "xs:boolean", /* Boolean */
+ "xs:byte", /* SByte */
+ "xs:unsignedByte", /* Byte */
+ "xs:short", /* Int16 */
+ "xs:unsignedShort", /* UInt16 */
+ "xs:int", /* Int32 */
+ "xs:unsignedInt", /* UInt32 */
+ "xs:long", /* Int64 */
+ "xs:unsignedLong", /* UInt64 */
+ "xs:float", /* Float */
+ "xs:double", /* Double */
+ "xs:string", /* String */
+ "xs:dateTime", /* DateTime */
+ "tns:Guid", /* Guid */
+ "xs:base64Binary", /* ByteString */
+ "tns:XmlElement", /* XmlElement */
+ "tns:NodeId", /* NodeId */
+ "tns:ExpandedNodeId", /* ExpandedNodeId */
+ "tns:StatusCode", /* StatusCode */
+ "tns:QualifiedName", /* QualifiedName */
+ "tns:LocalizedText", /* LocalizedText */
+ "tns:ExtensionObject", /* ExtensionObject */
+ "tns:DataValue", /* DataValue */
+ "tns:Variant", /* Variant */
+ "tns:DiagnosticInfo", /* DiagnosticInfo */
+ "tns:Decimal", /* Decimal */
+ NULL, /* Enum */
+ NULL, /* Structure */
+ NULL, /* Structure with optional fields */
+ NULL, /* Union */
+ NULL /* BitfieldCluster */
+};
+
/************/
/* Encoding */
/************/
@@ -160,6 +207,15 @@ static const char* UA_XML_EXTENSIONOBJECT_BYTESTRING = "ByteString";
(type->typeKind < UA_DATATYPEKIND_GUID || \
type->typeKind == UA_DATATYPEKIND_BYTESTRING)
+static bool
+isNullXml(const void *p, const UA_DataType *type) {
+ if(UA_DataType_isNumeric(type) || type->typeKind == UA_DATATYPEKIND_BOOLEAN)
+ return false;
+ UA_STACKARRAY(char, buf, type->memSize);
+ memset(buf, 0, type->memSize);
+ return (UA_order(buf, p, type) == UA_ORDER_EQ);
+}
+
static status UA_FUNC_ATTR_WARN_UNUSED_RESULT
xmlEncodeWriteChars(CtxXml *ctx, const char *c, size_t len) {
if(ctx->pos + len > ctx->end)
@@ -533,6 +589,62 @@ ENCODE_XML(ExtensionObject) {
return ret;
}
+static status
+Array_encodeXml(CtxXml *ctx, const void *ptr, size_t length,
+ const UA_DataType *type) {
+ size_t arrNameLen = strlen("ListOf") + strlen(type->typeName);
+ char* arrName = (char*)UA_malloc((arrNameLen + 1) * sizeof(char));
+ arrName[0] = 'L';
+ arrName[1] = 'i';
+ arrName[2] = 's';
+ arrName[3] = 't';
+ arrName[4] = 'O';
+ arrName[5] = 'f';
+ for(size_t i = 0; i < strlen(type->typeName); ++i)
+ arrName[strlen("ListOf") + i] = type->typeName[i];
+ arrName[arrNameLen] = '\0';
+
+ status ret = writeXmlElemNameBegin(ctx, arrName);
+
+ if(!ptr)
+ goto finish;
+
+ uintptr_t uptr = (uintptr_t)ptr;
+ for(size_t i = 0; i < length && ret == UA_STATUSCODE_GOOD; ++i) {
+ if(isNullXml((const void*)uptr, type))
+ ret |= xmlEncodeWriteChars(ctx, "null", 4);
+ else
+ ret |= writeXmlElement(ctx, type->typeName, (const void*)uptr, type);
+ uptr += type->memSize;
+ }
+
+finish:
+ ret |= writeXmlElemNameEnd(ctx, arrName, &UA_TYPES[UA_TYPES_VARIANT]);
+ UA_free(arrName);
+ return ret;
+}
+
+ENCODE_XML(Variant) {
+
+ if(!src->type)
+ return UA_STATUSCODE_BADENCODINGERROR;
+
+ /* Set the content type in the encoding mask */
+ const UA_Boolean isBuiltin = (src->type->typeKind <= UA_DATATYPEKIND_DIAGNOSTICINFO);
+
+ /* Set the array type in the encoding mask */
+ const bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
+
+ if((!isArray && !isBuiltin) || src->arrayDimensionsSize > 1)
+ return UA_STATUSCODE_BADNOTIMPLEMENTED;
+
+ status ret = writeXmlElemNameBegin(ctx, UA_XML_VARIANT_VALUE);
+
+ ret |= Array_encodeXml(ctx, src->data, src->arrayLength, src->type);
+
+ return ret | writeXmlElemNameEnd(ctx, UA_XML_VARIANT_VALUE, &UA_TYPES[UA_TYPES_VARIANT]);
+}
+
static status
encodeXmlNotImplemented(CtxXml *ctx, const void *src, const UA_DataType *type) {
(void)ctx, (void)src, (void)type;
@@ -563,7 +675,7 @@ const encodeXmlSignature encodeXmlJumpTable[UA_DATATYPEKINDS] = {
(encodeXmlSignature)LocalizedText_encodeXml, /* LocalizedText */
(encodeXmlSignature)ExtensionObject_encodeXml, /* ExtensionObject */
(encodeXmlSignature)encodeXmlNotImplemented, /* DataValue */
- (encodeXmlSignature)encodeXmlNotImplemented, /* Variant */
+ (encodeXmlSignature)Variant_encodeXml, /* Variant */
(encodeXmlSignature)encodeXmlNotImplemented, /* DiagnosticInfo */
(encodeXmlSignature)encodeXmlNotImplemented, /* Decimal */
(encodeXmlSignature)encodeXmlNotImplemented, /* Enum */
@@ -1471,8 +1583,36 @@ DECODE_XML(ExtensionObject) {
static status
Array_decodeXml(ParseCtxXml *ctx, void **dst, const UA_DataType *type) {
- (void)dst, (void)type, (void)ctx;
- return UA_STATUSCODE_BADNOTIMPLEMENTED;
+ if(strncmp(ctx->dataMembers[ctx->index]->name, "ListOf", strlen("ListOf")))
+ return UA_STATUSCODE_BADDECODINGERROR;
+
+ if(ctx->dataMembers[ctx->index]->type == XML_DATA_TYPE_PRIMITIVE) {
+ /* Return early for empty arrays */
+ *dst = UA_EMPTY_ARRAY_SENTINEL;
+ return UA_STATUSCODE_GOOD;
+ }
+
+ size_t length = ctx->dataMembers[ctx->index]->value.complex.membersSize;
+ ctx->index++; /* Go to first array member. */
+
+ /* Allocate memory */
+ *dst = UA_calloc(length, type->memSize);
+ if(*dst == NULL)
+ return UA_STATUSCODE_BADOUTOFMEMORY;
+
+ /* Decode array members */
+ uintptr_t ptr = (uintptr_t)*dst;
+ for(size_t i = 0; i < length; ++i) {
+ status ret = decodeXmlJumpTable[type->typeKind](ctx, (void*)ptr, type);
+ if(ret != UA_STATUSCODE_GOOD) {
+ UA_Array_delete(*dst, i + 1, type);
+ *dst = NULL;
+ return ret;
+ }
+ ptr += type->memSize;
+ }
+
+ return UA_STATUSCODE_GOOD;
}
static status
@@ -1545,17 +1685,19 @@ addNewXmlMember(XmlData *parent, const char *name) {
static void
deleteData(XmlData *data) {
- if(data->type == XML_DATA_TYPE_PRIMITIVE) {
- if(data->value.primitive.value != NULL)
- UA_free((void*)(uintptr_t)data->value.primitive.value);
+ if(data != NULL) {
+ if(data->type == XML_DATA_TYPE_PRIMITIVE) {
+ if(data->value.primitive.value != NULL)
+ UA_free((void*)(uintptr_t)data->value.primitive.value);
+ }
+ else {
+ for(size_t cnt = 0LU; cnt < data->value.complex.membersSize; ++cnt)
+ deleteData(data->value.complex.members[cnt]);
+ UA_free(data->value.complex.members);
+ }
+ UA_free((void*)(uintptr_t)data->name);
+ UA_free(data);
}
- else {
- for(size_t cnt = 0LU; cnt < data->value.complex.membersSize; ++cnt)
- deleteData(data->value.complex.members[cnt]);
- UA_free(data->value.complex.members);
- }
- UA_free((void*)(uintptr_t)data->name);
- UA_free(data);
}
static void
@@ -1669,7 +1811,6 @@ UA_decodeXml(const UA_ByteString *src, void *dst, const UA_DataType *type,
memset(&value, 0, sizeof(XmlValue));
memset(&parseCtx, 0, sizeof(XmlParsingCtx));
memset(&ctx, 0, sizeof(ParseCtxXml));
- memset(dst, 0, type->memSize);
ctx.dataMembers = (XmlData**)UA_malloc(UA_XML_MAXMEMBERSCOUNT * sizeof(XmlData*));
ctx.parseCtx = &parseCtx;
@@ -1689,12 +1830,12 @@ UA_decodeXml(const UA_ByteString *src, void *dst, const UA_DataType *type,
}
/* Decode */
- memset(dst, 0, type->memSize); /* Initialize the value */
-
if(ctx.value->isArray)
ret = Array_decodeXml(&ctx, (void**)dst, type);
- else
+ else {
+ memset(dst, 0, type->memSize); /* Initialize the value */
ret = decodeXmlJumpTable[type->typeKind](&ctx, dst, type);
+ }
if(ret != UA_STATUSCODE_GOOD) {
deleteData(ctx.value->data);
diff --git a/tests/check_types_builtin_xml.c b/tests/check_types_builtin_xml.c
index 60b8ff32b..19e29a3a4 100644
--- a/tests/check_types_builtin_xml.c
+++ b/tests/check_types_builtin_xml.c
@@ -1903,6 +1903,435 @@ START_TEST(UA_ExtensionObject_print_xml_encode) {
UA_ExtensionObject_delete(src);
}
+/* Array */
+START_TEST(UA_Array_Variant_Byte_xml_encode) {
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_Byte zero[2] = {42, 43};
+ UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_BYTE]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ const size_t variantXmlSectLen = 34;
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+ ck_assert_uint_eq(size,
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDefLen + variantXmlSectLen + 55);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ "42"
+ "43"
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_UInt16_xml_encode) {
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_UInt16 zero[2] = {42, 43};
+ UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_UINT16]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ const size_t variantXmlSectLen = 34;
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+ ck_assert_uint_eq(size,
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDefLen + variantXmlSectLen + 67);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ "42"
+ "43"
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_Null_xml_encode) {
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_Variant_setArray(src, NULL, 0, &UA_TYPES[UA_TYPES_UINT16]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_String_xml_encode) {
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_String zero[2] = {UA_STRING("eins"),UA_STRING("zwei")};
+ UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_STRING]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ "eins"
+ "zwei"
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+ UA_ByteString_clear(&buf);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_ExtensionObject_ByteString_xml_encode) {
+ UA_ExtensionObject eo1;
+ UA_ExtensionObject_init(&eo1);
+ eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+ eo1.content.encoded.typeId = UA_NODEID_NUMERIC(2, 1234);
+ eo1.content.encoded.body = UA_BYTESTRING_ALLOC("123456789012345678901234567890");
+
+ UA_ExtensionObject eo2;
+ UA_ExtensionObject_init(&eo2);
+ eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+ eo2.content.encoded.typeId = UA_NODEID_NUMERIC(3, 5678);
+ eo2.content.encoded.body = UA_BYTESTRING_ALLOC("98765432109876543210987654321");
+
+ UA_ExtensionObject eo3;
+ UA_ExtensionObject_init(&eo3);
+ eo3.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+ eo3.content.encoded.typeId = UA_NODEID_NUMERIC(4, 9999);
+ eo3.content.encoded.body = UA_BYTESTRING_ALLOC("1357911131517192123252729");
+
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_ExtensionObject zero[3] = {eo1, eo2, eo3};
+ UA_Variant_setArrayCopy(src, zero, 3, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ ""
+ ""
+ "ns=2;i=1234"
+ ""
+ ""
+ "123456789012345678901234567890"
+ ""
+ ""
+ ""
+ ""
+ "ns=3;i=5678"
+ ""
+ ""
+ "98765432109876543210987654321"
+ ""
+ ""
+ ""
+ ""
+ "ns=4;i=9999"
+ ""
+ ""
+ "1357911131517192123252729"
+ ""
+ ""
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_ExtensionObject_clear(&eo1);
+ UA_ExtensionObject_clear(&eo2);
+ UA_ExtensionObject_clear(&eo3);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_ExtensionObject_Xml_xml_encode) {
+ UA_ExtensionObject eo1;
+ UA_ExtensionObject_init(&eo1);
+ eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_XML;
+ eo1.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297);
+ eo1.content.encoded.body = UA_BYTESTRING_ALLOC(""
+ "ObjectToMoveOrCopy"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ "");
+
+ UA_ExtensionObject eo2;
+ UA_ExtensionObject_init(&eo2);
+ eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_XML;
+ eo2.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297);
+ eo2.content.encoded.body = UA_BYTESTRING_ALLOC(""
+ "TargetDirectory"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ "");
+
+ UA_ExtensionObject eo3;
+ UA_ExtensionObject_init(&eo3);
+ eo3.encoding = UA_EXTENSIONOBJECT_ENCODED_XML;
+ eo3.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297);
+ eo3.content.encoded.body = UA_BYTESTRING_ALLOC(""
+ "CreateCopy"
+ ""
+ "i=1"
+ ""
+ "-1"
+ ""
+ "");
+
+ UA_ExtensionObject eo4;
+ UA_ExtensionObject_init(&eo4);
+ eo4.encoding = UA_EXTENSIONOBJECT_ENCODED_XML;
+ eo4.content.encoded.typeId = UA_NODEID_NUMERIC(0, 297);
+ eo4.content.encoded.body = UA_BYTESTRING_ALLOC(""
+ "NewName"
+ ""
+ "i=12"
+ ""
+ "-1"
+ ""
+ "");
+
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_ExtensionObject zero[4] = {eo1, eo2, eo3, eo4};
+ UA_Variant_setArrayCopy(src, zero, 4, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ size_t size = UA_calcSizeXml((void*)src, type, NULL);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_encodeXml((void*)src, type, &buf, NULL);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "ObjectToMoveOrCopy"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "TargetDirectory"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "CreateCopy"
+ ""
+ "i=1"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "NewName"
+ ""
+ "i=12"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_ExtensionObject_clear(&eo1);
+ UA_ExtensionObject_clear(&eo2);
+ UA_ExtensionObject_clear(&eo3);
+ UA_ExtensionObject_clear(&eo4);
+ UA_Variant_delete(src);
+}
+END_TEST
+
+START_TEST(UA_Array_Variant_ExtensionObject_print_xml_encode) {
+ UA_ExtensionObject eo1;
+ UA_ExtensionObject_init(&eo1);
+ eo1.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+ eo1.content.encoded.typeId = UA_NODEID_NUMERIC(2, 1234);
+ eo1.content.encoded.body = UA_BYTESTRING_ALLOC("123456789012345678901234567890");
+
+ UA_ExtensionObject eo2;
+ UA_ExtensionObject_init(&eo2);
+ eo2.encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
+ eo2.content.encoded.typeId = UA_NODEID_NUMERIC(3, 5678);
+ eo2.content.encoded.body = UA_BYTESTRING_ALLOC("98765432109876543210987654321");
+
+ UA_Variant *src = UA_Variant_new();
+ UA_Variant_init(src);
+ UA_ExtensionObject zero[2] = {eo1, eo2};
+ UA_Variant_setArrayCopy(src, zero, 2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+
+ UA_EncodeXmlOptions options;
+ memset(&options, 0, sizeof(UA_EncodeXmlOptions));
+ options.prettyPrint = true;
+ const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
+ size_t size = UA_calcSizeXml((void*)src, type, &options);
+
+ UA_ByteString buf;
+ UA_ByteString_allocBuffer(&buf, size + 1);
+
+ status s = UA_printXml((void*)src, type, &buf);
+ ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
+
+ char result[size + 1];
+ sprintf(result, "%s"
+ "\n"
+ "\n\t"
+ "\n\t\t"
+ "\n\t\t\t"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t\tns=2;i=1234"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t\t123456789012345678901234567890"
+ "\n\t\t\t\t"
+ "\n\t\t\t"
+ "\n\t\t\t"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t\tns=3;i=5678"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t"
+ "\n\t\t\t\t\t98765432109876543210987654321"
+ "\n\t\t\t\t"
+ "\n\t\t\t"
+ "\n\t\t"
+ "\n\t"
+ "\n",
+ xmlEncTypeDefs[type->typeKind].xmlEncTypeDef);
+ buf.data[size] = 0; /* zero terminate */
+ ck_assert_str_eq(result, (char*)buf.data);
+
+ UA_ByteString_clear(&buf);
+ UA_ExtensionObject_clear(&eo1);
+ UA_ExtensionObject_clear(&eo2);
+ UA_Variant_delete(src);
+}
+END_TEST
+
// ---------------------------DECODE-------------------------------------
@@ -3708,6 +4137,247 @@ START_TEST(UA_ExtensionObject_InvalidBody_xml_decode) {
}
END_TEST
+/* Array */
+START_TEST(UA_Array_Boolean_xml_decode) {
+ UA_Boolean *out;
+ UA_ByteString buf = UA_STRING(""
+ "true"
+ "false"
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_BOOLEAN], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+ ck_assert_int_eq(out[0], true);
+ ck_assert_int_eq(out[1], false);
+
+ UA_Array_delete(out, 2, &UA_TYPES[UA_TYPES_BOOLEAN]);
+}
+END_TEST
+
+START_TEST(UA_Array_Number_xml_decode) {
+ UA_UInt32 *out;
+ UA_ByteString buf = UA_STRING(""
+ "0"
+ "1"
+ "2"
+ "3"
+ "4"
+ "5"
+ "6"
+ "7"
+ "8"
+ "9"
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+ for(size_t i = 0; i < 10; ++i)
+ ck_assert_int_eq(out[i], (UA_UInt32)i);
+
+ UA_Array_delete(out, 10, &UA_TYPES[UA_TYPES_UINT32]);
+}
+END_TEST
+
+START_TEST(UA_Array_Null_xml_decode) {
+ UA_UInt32 *out;
+ UA_ByteString buf = UA_STRING(""
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_UINT32], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+ ck_assert_ptr_eq(out, (UA_UInt32*)UA_EMPTY_ARRAY_SENTINEL);
+}
+END_TEST
+
+START_TEST(UA_Array_String_xml_decode) {
+ UA_String *out;
+ UA_ByteString buf = UA_STRING(""
+ "eins"
+ "zwei"
+ "drei"
+ "vier"
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_STRING], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+ UA_String testData1 = UA_STRING("eins");
+ UA_String testData2 = UA_STRING("zwei");
+ UA_String testData3 = UA_STRING("drei");
+ UA_String testData4 = UA_STRING("vier");
+ ck_assert(UA_String_equal(&out[0], &testData1));
+ ck_assert(UA_String_equal(&out[1], &testData2));
+ ck_assert(UA_String_equal(&out[2], &testData3));
+ ck_assert(UA_String_equal(&out[3], &testData4));
+
+ UA_Array_delete(out, 4, &UA_TYPES[UA_TYPES_STRING]);
+}
+END_TEST
+
+START_TEST(UA_Array_ExtensionObject_ByteString_xml_decode) {
+ UA_ExtensionObject *out;
+ UA_ByteString buf = UA_STRING(""
+ ""
+ ""
+ "ns=2;i=1234"
+ ""
+ ""
+ "123456789012345678901234567890"
+ ""
+ ""
+ ""
+ ""
+ "ns=3;i=5678"
+ ""
+ ""
+ "98765432109876543210987654321"
+ ""
+ ""
+ ""
+ ""
+ "ns=4;i=9999"
+ ""
+ ""
+ "1357911131517192123252729"
+ ""
+ ""
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+ ck_assert_int_eq(out[0].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING);
+ UA_String testData1 = UA_STRING("123456789012345678901234567890");
+ ck_assert(UA_String_equal(&out[0].content.encoded.body, &testData1));
+ ck_assert_int_eq(out[0].content.encoded.typeId.namespaceIndex, 2);
+ ck_assert_int_eq(out[0].content.encoded.typeId.identifier.numeric, 1234);
+ ck_assert_int_eq(out[1].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING);
+ UA_String testData2 = UA_STRING("98765432109876543210987654321");
+ ck_assert(UA_String_equal(&out[1].content.encoded.body, &testData2));
+ ck_assert_int_eq(out[1].content.encoded.typeId.namespaceIndex, 3);
+ ck_assert_int_eq(out[1].content.encoded.typeId.identifier.numeric, 5678);
+ ck_assert_int_eq(out[2].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING);
+ UA_String testData3 = UA_STRING("1357911131517192123252729");
+ ck_assert(UA_String_equal(&out[2].content.encoded.body, &testData3));
+ ck_assert_int_eq(out[2].content.encoded.typeId.namespaceIndex, 4);
+ ck_assert_int_eq(out[2].content.encoded.typeId.identifier.numeric, 9999);
+
+ UA_Array_delete(out, 3, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+}
+END_TEST
+
+START_TEST(UA_Array_ExtensionObject_Xml_ByteString_xml_decode) {
+ UA_ExtensionObject *out;
+ UA_ByteString buf = UA_STRING(""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "ObjectToMoveOrCopy"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "TargetDirectory"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "i=297"
+ ""
+ ""
+ ""
+ "CreateCopy"
+ ""
+ "i=1"
+ ""
+ "-1"
+ ""
+ ""
+ ""
+ ""
+ ""
+ ""
+ "ns=4;i=9999"
+ ""
+ ""
+ "1357911131517192123252729"
+ ""
+ ""
+ "");
+
+ UA_StatusCode retval = UA_decodeXml(&buf, &out, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT], NULL);
+
+ ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+ ck_assert_int_eq(out[0].encoding, UA_EXTENSIONOBJECT_ENCODED_XML);
+ UA_String testData1 = UA_STRING(""
+ "ObjectToMoveOrCopy"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ "");
+ ck_assert(UA_String_equal(&out[0].content.encoded.body, &testData1));
+ ck_assert_int_eq(out[0].content.encoded.typeId.namespaceIndex, 0);
+ ck_assert_int_eq(out[0].content.encoded.typeId.identifier.numeric, 297);
+ ck_assert_int_eq(out[1].encoding, UA_EXTENSIONOBJECT_ENCODED_XML);
+ UA_String testData2 = UA_STRING(""
+ "TargetDirectory"
+ ""
+ "i=17"
+ ""
+ "-1"
+ ""
+ "");
+ ck_assert(UA_String_equal(&out[1].content.encoded.body, &testData2));
+ ck_assert_int_eq(out[1].content.encoded.typeId.namespaceIndex, 0);
+ ck_assert_int_eq(out[1].content.encoded.typeId.identifier.numeric, 297);
+ ck_assert_int_eq(out[2].encoding, UA_EXTENSIONOBJECT_ENCODED_XML);
+ UA_String testData3 = UA_STRING(""
+ "CreateCopy"
+ ""
+ "i=1"
+ ""
+ "-1"
+ ""
+ "");
+ ck_assert(UA_String_equal(&out[2].content.encoded.body, &testData3));
+ ck_assert_int_eq(out[2].content.encoded.typeId.namespaceIndex, 0);
+ ck_assert_int_eq(out[2].content.encoded.typeId.identifier.numeric, 297);
+ ck_assert_int_eq(out[3].encoding, UA_EXTENSIONOBJECT_ENCODED_BYTESTRING);
+ UA_String testData4 = UA_STRING("1357911131517192123252729");
+ ck_assert(UA_String_equal(&out[3].content.encoded.body, &testData4));
+ ck_assert_int_eq(out[3].content.encoded.typeId.namespaceIndex, 4);
+ ck_assert_int_eq(out[3].content.encoded.typeId.identifier.numeric, 9999);
+
+ UA_Array_delete(out, 4, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
+}
+END_TEST
+
static Suite *testSuite_builtin_xml(void) {
Suite *s = suite_create("Built-in Data Types 62541-5 Xml");
@@ -3814,6 +4484,14 @@ static Suite *testSuite_builtin_xml(void) {
tcase_add_test(tc_xml_encode, UA_ExtensionObject_null_xml_encode);
tcase_add_test(tc_xml_encode, UA_ExtensionObject_print_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_Byte_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_UInt16_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_Null_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_String_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_ByteString_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_Xml_xml_encode);
+ tcase_add_test(tc_xml_encode, UA_Array_Variant_ExtensionObject_print_xml_encode);
+
suite_add_tcase(s, tc_xml_encode);
TCase *tc_xml_decode = tcase_create("xml_decode");
@@ -3922,6 +4600,13 @@ static Suite *testSuite_builtin_xml(void) {
tcase_add_test(tc_xml_decode, UA_ExtensionObject_EncodedXml_4_xml_decode);
tcase_add_test(tc_xml_decode, UA_ExtensionObject_InvalidBody_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_Boolean_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_Number_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_Null_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_String_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_ExtensionObject_ByteString_xml_decode);
+ tcase_add_test(tc_xml_decode, UA_Array_ExtensionObject_Xml_ByteString_xml_decode);
+
suite_add_tcase(s, tc_xml_decode);
return s;