This is an automated email from the ASF dual-hosted git repository.

tiagobento pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git


The following commit(s) were added to refs/heads/main by this push:
     new afdb6c44981 kie-issues#1017: `@kie-tools/dmn-marshaller` not 
respecting `xsd:sequence` declaration order when building XML from JSON (#2203)
afdb6c44981 is described below

commit afdb6c44981ad79eca7052a1d6f1f79c1f2a6172
Author: Tiago Bento <[email protected]>
AuthorDate: Wed Mar 20 12:02:19 2024 -0400

    kie-issues#1017: `@kie-tools/dmn-marshaller` not respecting `xsd:sequence` 
declaration order when building XML from JSON (#2203)
---
 .../tests-data--manual/other/decisionAndInput.dmn  |  52 +++++
 .../other/decisionAndInput_wrongSequenceOrder.dmn  |  51 +++++
 packages/dmn-marshaller/tests/xsdSequence.test.ts  |  94 ++++++++
 packages/xml-parser-ts-codegen/src/codegen.ts      | 246 +++++++++++----------
 packages/xml-parser-ts/src/idRandomizer.ts         |   4 +-
 packages/xml-parser-ts/src/index.ts                | 115 +++++++---
 6 files changed, 412 insertions(+), 150 deletions(-)

diff --git 
a/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput.dmn 
b/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput.dmn
new file mode 100644
index 00000000000..575cff37382
--- /dev/null
+++ b/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput.dmn
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/";
+    expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/";
+    namespace="https://kie.org/dmn/_D19C1092-7677-427F-A493-BCED38F74A9B";
+    id="_11655DE3-BEA5-45B1-B54E-8AD84FBBED25" 
name="DMN_1E889EDB-B967-4508-8DB1-E0DF5986E62F"
+    xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/";
+    xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/";
+    xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/";
+    xmlns:kie="https://kie.org/dmn/extensions/1.0";>
+    <inputData name="New Input Data" 
id="_154F9E03-B180-4C87-B7D3-8745DA4336F4">
+        <variable name="New Input Data" 
id="_A28401DD-9A87-4251-A1E4-C63FC3A7C729" typeRef="string" />
+    </inputData>
+    <decision name="New Decision" id="_392BEF3D-44B5-47DC-8A06-C36F15DB2984">
+        <variable id="_C2C9C21A-E708-46D9-876A-52BB25692B66" typeRef="string" 
name="New Decision" />
+        <informationRequirement id="_E781E253-D97E-4A1D-BE51-037B012B30F0">
+            <requiredInput href="#_154F9E03-B180-4C87-B7D3-8745DA4336F4" />
+        </informationRequirement>
+        <literalExpression id="_509ED9AF-3852-48F4-89A7-3CCF221B809C" 
label="New Decision"
+            typeRef="string">
+            <text>&quot;New Decision&quot;</text>
+        </literalExpression>
+    </decision>
+    <dmndi:DMNDI>
+        <dmndi:DMNDiagram id="_0D2FD42B-91FF-4795-B71F-E501CE115389" 
name="Default DRD"
+            useAlternativeInputDataShape="false">
+            <di:extension>
+                <kie:ComponentsWidthsExtension>
+                    <kie:ComponentWidths 
dmnElementRef="_509ED9AF-3852-48F4-89A7-3CCF221B809C">
+                        <kie:width>190</kie:width>
+                    </kie:ComponentWidths>
+                </kie:ComponentsWidthsExtension>
+            </di:extension>
+            <dmndi:DMNShape id="_92B3305F-A892-4E38-BD92-398906A9BC24"
+                dmnElementRef="_154F9E03-B180-4C87-B7D3-8745DA4336F4" 
isCollapsed="false"
+                isListedInputData="false">
+                <dc:Bounds x="100" y="280" width="160" height="80" />
+            </dmndi:DMNShape>
+            <dmndi:DMNShape id="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2"
+                dmnElementRef="_392BEF3D-44B5-47DC-8A06-C36F15DB2984" 
isCollapsed="false"
+                isListedInputData="false">
+                <dc:Bounds x="100" y="100" width="160" height="80" />
+            </dmndi:DMNShape>
+            <dmndi:DMNEdge 
id="_C54C8ED9-7DB2-47BC-844A-E79D7142844B-AUTO-TARGET"
+                dmnElementRef="_E781E253-D97E-4A1D-BE51-037B012B30F0"
+                sourceElement="_92B3305F-A892-4E38-BD92-398906A9BC24"
+                targetElement="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2">
+                <di:waypoint x="180" y="320" />
+                <di:waypoint x="180" y="140" />
+            </dmndi:DMNEdge>
+        </dmndi:DMNDiagram>
+    </dmndi:DMNDI>
+</definitions>
\ No newline at end of file
diff --git 
a/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput_wrongSequenceOrder.dmn
 
b/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput_wrongSequenceOrder.dmn
new file mode 100644
index 00000000000..26f3fa00fae
--- /dev/null
+++ 
b/packages/dmn-marshaller/tests-data--manual/other/decisionAndInput_wrongSequenceOrder.dmn
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/";
+  expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/";
+  namespace="https://kie.org/dmn/_D19C1092-7677-427F-A493-BCED38F74A9B";
+  id="_11655DE3-BEA5-45B1-B54E-8AD84FBBED25" 
name="DMN_1E889EDB-B967-4508-8DB1-E0DF5986E62F"
+  xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/";
+  xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/";
+  xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:kie="https://kie.org/dmn/extensions/1.0";>
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_0D2FD42B-91FF-4795-B71F-E501CE115389" name="Default 
DRD"
+      useAlternativeInputDataShape="false">
+      <dmndi:DMNShape id="_92B3305F-A892-4E38-BD92-398906A9BC24"
+        dmnElementRef="_154F9E03-B180-4C87-B7D3-8745DA4336F4" 
isCollapsed="false"
+        isListedInputData="false">
+        <dc:Bounds x="100" y="280" width="160" height="80" />
+      </dmndi:DMNShape>
+      <dmndi:DMNShape id="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2"
+        dmnElementRef="_392BEF3D-44B5-47DC-8A06-C36F15DB2984" 
isCollapsed="false"
+        isListedInputData="false">
+        <dc:Bounds x="100" y="100" width="160" height="80" />
+      </dmndi:DMNShape>
+      <dmndi:DMNEdge id="_C54C8ED9-7DB2-47BC-844A-E79D7142844B-AUTO-TARGET"
+        dmnElementRef="_E781E253-D97E-4A1D-BE51-037B012B30F0"
+        sourceElement="_92B3305F-A892-4E38-BD92-398906A9BC24"
+        targetElement="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2">
+        <di:waypoint x="180" y="320" />
+        <di:waypoint x="180" y="140" />
+      </dmndi:DMNEdge>
+      <di:extension>
+        <kie:ComponentsWidthsExtension>
+          <kie:ComponentWidths 
dmnElementRef="_509ED9AF-3852-48F4-89A7-3CCF221B809C">
+            <kie:width>190</kie:width>
+          </kie:ComponentWidths>
+        </kie:ComponentsWidthsExtension>
+      </di:extension>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+  <inputData name="New Input Data" id="_154F9E03-B180-4C87-B7D3-8745DA4336F4">
+    <variable name="New Input Data" id="_A28401DD-9A87-4251-A1E4-C63FC3A7C729" 
typeRef="string" />
+  </inputData>
+  <decision name="New Decision" id="_392BEF3D-44B5-47DC-8A06-C36F15DB2984">
+    <literalExpression id="_509ED9AF-3852-48F4-89A7-3CCF221B809C" label="New 
Decision"
+      typeRef="string">
+      <text>&quot;New Decision&quot;</text>
+    </literalExpression>
+    <variable id="_C2C9C21A-E708-46D9-876A-52BB25692B66" typeRef="string" 
name="New Decision" />
+    <informationRequirement id="_E781E253-D97E-4A1D-BE51-037B012B30F0">
+      <requiredInput href="#_154F9E03-B180-4C87-B7D3-8745DA4336F4" />
+    </informationRequirement>
+  </decision>
+</definitions>
\ No newline at end of file
diff --git a/packages/dmn-marshaller/tests/xsdSequence.test.ts 
b/packages/dmn-marshaller/tests/xsdSequence.test.ts
new file mode 100644
index 00000000000..ca831d5ffad
--- /dev/null
+++ b/packages/dmn-marshaller/tests/xsdSequence.test.ts
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as fs from "fs";
+import * as path from "path";
+import { getMarshaller } from "@kie-tools/dmn-marshaller";
+
+const files = [
+  { path: "../tests-data--manual/other/decisionAndInput.dmn" },
+  { path: 
"../tests-data--manual/other/decisionAndInput_wrongSequenceOrder.dmn" },
+];
+
+describe("build always produces elements in the same order", () => {
+  for (const file of files) {
+    test(path.basename(file.path), () => {
+      const xml = fs.readFileSync(path.join(__dirname, file.path), "utf-8");
+      const marshaller = getMarshaller(xml, { upgradeTo: "1.5" });
+      const json = marshaller.parser.parse();
+
+      // Adding some props in an arbitrary order shouldn't alter the end 
result.
+
+      json.definitions.import = [
+        {
+          "@_name": "some-import",
+          "@_namespace": "some-namespace",
+          "@_importType": "some-import-type",
+        },
+      ];
+
+      json.definitions.artifact = [
+        {
+          __$$element: "group",
+          "@_name": "some-group",
+        },
+      ];
+
+      expect(marshaller.builder.build(json)).toStrictEqual(`<?xml 
version="1.0" encoding="UTF-8" ?>
+<definitions xmlns="https://www.omg.org/spec/DMN/20230324/MODEL/"; 
expressionLanguage="https://www.omg.org/spec/DMN/20230324/FEEL/"; 
namespace="https://kie.org/dmn/_D19C1092-7677-427F-A493-BCED38F74A9B"; 
id="_11655DE3-BEA5-45B1-B54E-8AD84FBBED25" 
name="DMN_1E889EDB-B967-4508-8DB1-E0DF5986E62F" 
xmlns:dmndi="https://www.omg.org/spec/DMN/20230324/DMNDI/"; 
xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/"; 
xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/"; 
xmlns:kie="https://kie.org/dmn/extens [...]
+  <import name="some-import" namespace="some-namespace" 
importType="some-import-type" />
+  <inputData name="New Input Data" id="_154F9E03-B180-4C87-B7D3-8745DA4336F4">
+    <variable name="New Input Data" id="_A28401DD-9A87-4251-A1E4-C63FC3A7C729" 
typeRef="string" />
+  </inputData>
+  <decision name="New Decision" id="_392BEF3D-44B5-47DC-8A06-C36F15DB2984">
+    <variable id="_C2C9C21A-E708-46D9-876A-52BB25692B66" typeRef="string" 
name="New Decision" />
+    <informationRequirement id="_E781E253-D97E-4A1D-BE51-037B012B30F0">
+      <requiredInput href="#_154F9E03-B180-4C87-B7D3-8745DA4336F4" />
+    </informationRequirement>
+    <literalExpression id="_509ED9AF-3852-48F4-89A7-3CCF221B809C" label="New 
Decision" typeRef="string">
+      <text>&quot;New Decision&quot;</text>
+    </literalExpression>
+  </decision>
+  <group name="some-group" />
+  <dmndi:DMNDI>
+    <dmndi:DMNDiagram id="_0D2FD42B-91FF-4795-B71F-E501CE115389" name="Default 
DRD" useAlternativeInputDataShape="false">
+      <di:extension>
+        <kie:ComponentsWidthsExtension>
+          <kie:ComponentWidths 
dmnElementRef="_509ED9AF-3852-48F4-89A7-3CCF221B809C">
+            <kie:width>190</kie:width>
+          </kie:ComponentWidths>
+        </kie:ComponentsWidthsExtension>
+      </di:extension>
+      <dmndi:DMNShape id="_92B3305F-A892-4E38-BD92-398906A9BC24" 
dmnElementRef="_154F9E03-B180-4C87-B7D3-8745DA4336F4" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="100" y="280" width="160" height="80" />
+      </dmndi:DMNShape>
+      <dmndi:DMNShape id="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2" 
dmnElementRef="_392BEF3D-44B5-47DC-8A06-C36F15DB2984" isCollapsed="false" 
isListedInputData="false">
+        <dc:Bounds x="100" y="100" width="160" height="80" />
+      </dmndi:DMNShape>
+      <dmndi:DMNEdge id="_C54C8ED9-7DB2-47BC-844A-E79D7142844B-AUTO-TARGET" 
dmnElementRef="_E781E253-D97E-4A1D-BE51-037B012B30F0" 
sourceElement="_92B3305F-A892-4E38-BD92-398906A9BC24" 
targetElement="_1E49EEEB-9296-4AE5-B37C-2EE0044C0CC2">
+        <di:waypoint x="180" y="320" />
+        <di:waypoint x="180" y="140" />
+      </dmndi:DMNEdge>
+    </dmndi:DMNDiagram>
+  </dmndi:DMNDI>
+</definitions>
+`);
+    });
+  }
+});
diff --git a/packages/xml-parser-ts-codegen/src/codegen.ts 
b/packages/xml-parser-ts-codegen/src/codegen.ts
index 179531fee23..eb349425f85 100644
--- a/packages/xml-parser-ts-codegen/src/codegen.ts
+++ b/packages/xml-parser-ts-codegen/src/codegen.ts
@@ -578,128 +578,6 @@ function getMetaProperties(
   const metaProperties: XptcMetaTypeProperty[] = [];
   const anonymousTypes: XptcMetaType[] = [];
 
-  for (const a of ct.attributes) {
-    const attributeType = getTsTypeFromLocalRef(
-      __XSDS,
-      __NAMED_TYPES_BY_TS_NAME,
-      ct.declaredAtRelativeLocation,
-      a.localTypeRef
-    );
-
-    metaProperties.push({
-      declaredAt: ct.declaredAtRelativeLocation,
-      fromType: metaTypeName,
-      name: `@_${a.name}`,
-      elem: undefined,
-      metaType: { name: getMetaTypeName(attributeType), xsdType: 
attributeType.annotation },
-      isArray: false,
-      isOptional: a.isOptional,
-    });
-  }
-
-  for (const e of ct.elements) {
-    if (e.kind === "ofRef") {
-      const referencedElement = getXptcElementFromLocalElementRef(
-        __XSDS,
-        __GLOBAL_ELEMENTS,
-        ct.declaredAtRelativeLocation,
-        e.ref
-      );
-
-      if (!referencedElement) {
-        throw new Error(`Can't find reference to element '${e.ref}'`);
-      }
-
-      const tsType = referencedElement.type
-        ? getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, referencedElement.type)
-        : {
-            name: getTsNameFromNamedType(
-              ct.declaredAtRelativeLocation,
-              getAnonymousMetaTypeName(referencedElement.name, "GLOBAL")
-            ),
-            annotation: "Anonymous type from element " + 
referencedElement.name,
-          };
-
-      metaProperties.push({
-        declaredAt: referencedElement?.declaredAtRelativeLocation,
-        fromType: ct.isAnonymous ? "" : metaTypeName,
-        name: referencedElement.name,
-        elem: referencedElement,
-        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation 
},
-        typeBody: () =>
-          getTypeBodyForElementRef(
-            __RELATIVE_LOCATION,
-            __META_TYPE_MAPPING,
-            __GLOBAL_ELEMENTS,
-            __SUBSTITUTIONS,
-            __XSDS,
-            __NAMED_TYPES_BY_TS_NAME,
-            ct,
-            referencedElement
-          ),
-        isArray: e.isArray,
-        isOptional: e.isOptional,
-      });
-    } else if (e.kind === "ofNamedType") {
-      const tsType = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, e.typeName);
-      metaProperties.push({
-        declaredAt: ct.declaredAtRelativeLocation,
-        fromType: metaTypeName,
-        name: e.name,
-        elem: undefined, // REALLY?
-        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation 
},
-        typeBody: getTsTypeBody(tsType),
-        isArray: e.isArray,
-        isOptional: e.isOptional,
-      });
-    } else if (e.kind === "ofAnonymousType") {
-      const anonymousTypeName = getAnonymousMetaTypeName(e.name, metaTypeName);
-      const mp = getMetaProperties(
-        __RELATIVE_LOCATION,
-        __META_TYPE_MAPPING,
-        __GLOBAL_ELEMENTS,
-        __SUBSTITUTIONS,
-        __XSDS,
-        __NAMED_TYPES_BY_TS_NAME,
-        e.anonymousType,
-        anonymousTypeName
-      );
-      anonymousTypes.push({ name: anonymousTypeName, properties: 
mp.metaProperties });
-      anonymousTypes.push(...mp.anonymousTypes);
-      __META_TYPE_MAPPING.set(anonymousTypeName, {
-        name: anonymousTypeName,
-        properties: mp.metaProperties,
-      });
-      metaProperties.push({
-        declaredAt: ct.declaredAtRelativeLocation,
-        fromType: metaTypeName,
-        name: e.name,
-        elem: undefined, // REALLY?
-        metaType: { name: anonymousTypeName, xsdType: "Anonymous type..." },
-        isArray: e.isArray,
-        isOptional: e.isOptional,
-      });
-    } else {
-      throw new Error(`Unknown kind of XptcComplexType '${e}'`);
-    }
-  }
-
-  if (ct.isSimpleContent && ct.childOf) {
-    const t = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, ct.childOf);
-    metaProperties.push({
-      declaredAt: ct.declaredAtRelativeLocation,
-      fromType: metaTypeName,
-      name: `__$$text`,
-      elem: undefined,
-      metaType: {
-        name: t.name,
-        xsdType: t.annotation,
-      },
-      isArray: false,
-      isOptional: false,
-    });
-  }
-
   const immediateParentType = ct.childOf
     ? getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, ct.childOf)
     : undefined;
@@ -855,6 +733,130 @@ function getMetaProperties(
     }
   }
 
+  // Own properties are parsed later to ensure xsd:sequence order.
+
+  for (const a of ct.attributes) {
+    const attributeType = getTsTypeFromLocalRef(
+      __XSDS,
+      __NAMED_TYPES_BY_TS_NAME,
+      ct.declaredAtRelativeLocation,
+      a.localTypeRef
+    );
+
+    metaProperties.push({
+      declaredAt: ct.declaredAtRelativeLocation,
+      fromType: metaTypeName,
+      name: `@_${a.name}`,
+      elem: undefined,
+      metaType: { name: getMetaTypeName(attributeType), xsdType: 
attributeType.annotation },
+      isArray: false,
+      isOptional: a.isOptional,
+    });
+  }
+
+  for (const e of ct.elements) {
+    if (e.kind === "ofRef") {
+      const referencedElement = getXptcElementFromLocalElementRef(
+        __XSDS,
+        __GLOBAL_ELEMENTS,
+        ct.declaredAtRelativeLocation,
+        e.ref
+      );
+
+      if (!referencedElement) {
+        throw new Error(`Can't find reference to element '${e.ref}'`);
+      }
+
+      const tsType = referencedElement.type
+        ? getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, referencedElement.type)
+        : {
+            name: getTsNameFromNamedType(
+              ct.declaredAtRelativeLocation,
+              getAnonymousMetaTypeName(referencedElement.name, "GLOBAL")
+            ),
+            annotation: "Anonymous type from element " + 
referencedElement.name,
+          };
+
+      metaProperties.push({
+        declaredAt: referencedElement?.declaredAtRelativeLocation,
+        fromType: ct.isAnonymous ? "" : metaTypeName,
+        name: referencedElement.name,
+        elem: referencedElement,
+        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation 
},
+        typeBody: () =>
+          getTypeBodyForElementRef(
+            __RELATIVE_LOCATION,
+            __META_TYPE_MAPPING,
+            __GLOBAL_ELEMENTS,
+            __SUBSTITUTIONS,
+            __XSDS,
+            __NAMED_TYPES_BY_TS_NAME,
+            ct,
+            referencedElement
+          ),
+        isArray: e.isArray,
+        isOptional: e.isOptional,
+      });
+    } else if (e.kind === "ofNamedType") {
+      const tsType = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, e.typeName);
+      metaProperties.push({
+        declaredAt: ct.declaredAtRelativeLocation,
+        fromType: metaTypeName,
+        name: e.name,
+        elem: undefined, // REALLY?
+        metaType: { name: getMetaTypeName(tsType), xsdType: tsType.annotation 
},
+        typeBody: getTsTypeBody(tsType),
+        isArray: e.isArray,
+        isOptional: e.isOptional,
+      });
+    } else if (e.kind === "ofAnonymousType") {
+      const anonymousTypeName = getAnonymousMetaTypeName(e.name, metaTypeName);
+      const mp = getMetaProperties(
+        __RELATIVE_LOCATION,
+        __META_TYPE_MAPPING,
+        __GLOBAL_ELEMENTS,
+        __SUBSTITUTIONS,
+        __XSDS,
+        __NAMED_TYPES_BY_TS_NAME,
+        e.anonymousType,
+        anonymousTypeName
+      );
+      anonymousTypes.push({ name: anonymousTypeName, properties: 
mp.metaProperties });
+      anonymousTypes.push(...mp.anonymousTypes);
+      __META_TYPE_MAPPING.set(anonymousTypeName, {
+        name: anonymousTypeName,
+        properties: mp.metaProperties,
+      });
+      metaProperties.push({
+        declaredAt: ct.declaredAtRelativeLocation,
+        fromType: metaTypeName,
+        name: e.name,
+        elem: undefined, // REALLY?
+        metaType: { name: anonymousTypeName, xsdType: "Anonymous type..." },
+        isArray: e.isArray,
+        isOptional: e.isOptional,
+      });
+    } else {
+      throw new Error(`Unknown kind of XptcComplexType '${e}'`);
+    }
+  }
+
+  if (ct.isSimpleContent && ct.childOf) {
+    const t = getTsTypeFromLocalRef(__XSDS, __NAMED_TYPES_BY_TS_NAME, 
ct.declaredAtRelativeLocation, ct.childOf);
+    metaProperties.push({
+      declaredAt: ct.declaredAtRelativeLocation,
+      fromType: metaTypeName,
+      name: `__$$text`,
+      elem: undefined,
+      metaType: {
+        name: t.name,
+        xsdType: t.annotation,
+      },
+      isArray: false,
+      isOptional: false,
+    });
+  }
+
   if (!(ct.type === "complex" && !ct.isAnonymous && ct.isAbstract)) {
     __META_TYPE_MAPPING.set(metaTypeName, {
       name: metaTypeName,
diff --git a/packages/xml-parser-ts/src/idRandomizer.ts 
b/packages/xml-parser-ts/src/idRandomizer.ts
index 68cf95d9989..107c6c92b10 100644
--- a/packages/xml-parser-ts/src/idRandomizer.ts
+++ b/packages/xml-parser-ts/src/idRandomizer.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { Elements, Meta, MetaTypeDef } from ".";
+import { Elements, Meta, MetaType } from ".";
 import { buildXmlQName, parseXmlQName } from "./qNames";
 
 export type XmlParserTsIdRandomizerUpdater = (args: { newId: string }) => void;
@@ -26,7 +26,7 @@ export type XmlParserTsIdRandomizerMatcher<M extends Meta> = 
(args: {
   parentJson: any;
   attr: string;
   metaTypeName: keyof M;
-  metaType: Record<string, MetaTypeDef>;
+  metaType: MetaType;
 }) => [string, XmlParserTsIdRandomizerUpdater] | undefined;
 
 export class XmlParserTsIdRandomizer<M extends Meta> {
diff --git a/packages/xml-parser-ts/src/index.ts 
b/packages/xml-parser-ts/src/index.ts
index aa4a1f17230..8ade30121a3 100644
--- a/packages/xml-parser-ts/src/index.ts
+++ b/packages/xml-parser-ts/src/index.ts
@@ -36,9 +36,11 @@ export type XmlDocument = {
   };
 };
 
-export type MetaTypeDef = { type: string; isArray: boolean; fromType: string; 
xsdType: string };
+export type MetaTypeProp = { type: string; isArray: boolean; fromType: string; 
xsdType: string };
 
-export type Meta = Record<string, Record<string, MetaTypeDef>>;
+export type MetaType = Record<string, MetaTypeProp>;
+
+export type Meta = Record<string, MetaType>;
 
 export type Root = { element: string; type: string };
 
@@ -174,7 +176,20 @@ export function getParser<T extends object>(args: {
         "@_encoding": "UTF-8",
       };
 
-      const xml = build({ json: __json, ns: args.ns, instanceNs, indent: "" });
+      // Since building starts from a level above the root element, we need 
create this pseudo-metaType to correctly type the tree we're building.
+      const rootMetaType = {
+        [args.root.element]: { type: args.root.type, fromType: "root", 
isArray: false, xsdType: "// root" },
+      };
+
+      const xml = build({
+        json: __json,
+        ns: args.ns,
+        instanceNs,
+        elements: args.elements,
+        meta: args.meta,
+        metaType: rootMetaType,
+        indent: "",
+      });
       // console.timeEnd("building took");
       return xml;
     },
@@ -187,7 +202,7 @@ export function getParser<T extends object>(args: {
 
 export function parse(args: {
   node: Node;
-  nodeMetaType: Record<string, MetaTypeDef | undefined> | undefined;
+  nodeMetaType: MetaType | undefined;
   ns: Map<string, string>;
   instanceNs: Map<string, string>;
   meta: Meta;
@@ -283,7 +298,7 @@ export function parse(args: {
 
 export function resolveElement(
   name: string,
-  parentMetaType: Record<string, MetaTypeDef | undefined> | undefined,
+  parentMetaType: MetaType | undefined,
   {
     ns,
     instanceNs,
@@ -400,6 +415,7 @@ function buildAttrs(json: any) {
   let hasText = false;
   let attrs = " ";
 
+  // Attributes don't ever need to be serialized in a particular order.
   for (const propName in json) {
     if (propName[0] === "@") {
       attrs += `${propName.substring(2)}="${applyEntities(json[propName])}" `;
@@ -422,9 +438,12 @@ export function build(args: {
   json: any;
   ns: Map<string, string>;
   instanceNs: Map<string, string>;
+  elements: Elements;
+  meta: Meta;
+  metaType: MetaType | undefined;
   indent: string;
 }): string {
-  const { json, ns, instanceNs, indent } = args;
+  const { json, ns, instanceNs, indent, metaType } = args;
 
   if (typeof json !== "object" || json === null) {
     throw new Error(`Can't build XML from a non-object value. '${json}'.`);
@@ -432,40 +451,54 @@ export function build(args: {
 
   let xml = "";
 
-  for (const _propName in json) {
-    const propName = applyEntities(_propName);
-    const propValue = json[propName];
+  // We want to respect a certain order here given xsd:sequence declarations.
+  const sortedJsonProps: string[] = [];
+  for (const p in json) {
+    sortedJsonProps.push(p);
+  }
+
+  const declaredPropOrder: string[] = [];
+  for (const p in metaType ?? {}) {
+    declaredPropOrder.push(p);
+  }
+
+  sortedJsonProps.sort((a, b) => declaredPropOrder.indexOf(a) - 
declaredPropOrder.indexOf(b));
+
+  // After the correct order is established, we can iterate over the `json` 
object.
+  for (const __unsafeJsonPropName of sortedJsonProps) {
+    const jsonPropName = applyEntities(__unsafeJsonPropName);
+    const jsonPropValue = json[jsonPropName];
 
     // attributes are processed individually.
-    if (propName[0] === "@") {
+    if (jsonPropName[0] === "@") {
       continue;
     }
     // ignore this, as we won't make it part of the final XML.
-    else if (propName === "__$$element") {
+    else if (jsonPropName === "__$$element") {
       continue;
     }
     // ignore this. text content is treated inside the "array" and "nested 
element" sections.
-    else if (propName === "__$$text") {
+    else if (jsonPropName === "__$$text") {
       continue;
     }
     // pi tag
-    else if (propName[0] === "?") {
-      xml = `${indent}<${propName}${buildAttrs(propValue).attrs} ?>\n` + xml; 
// PI Tags should always go at the top of the XML
+    else if (jsonPropName[0] === "?") {
+      xml = `${indent}<${jsonPropName}${buildAttrs(jsonPropValue).attrs} ?>\n` 
+ xml; // PI Tags should always go at the top of the XML
     }
     // empty tag
-    else if (propValue === undefined || propValue === null || propValue === 
"") {
-      const elementName = applyInstanceNs({ ns, instanceNs, propName });
+    else if (jsonPropValue === undefined || jsonPropValue === null || 
jsonPropValue === "") {
+      const elementName = applyInstanceNs({ ns, instanceNs, propName: 
jsonPropName });
       xml += `${indent}<${elementName} />\n`;
     }
     // primitive element
-    else if (typeof propValue !== "object") {
-      const elementName = applyInstanceNs({ ns, instanceNs, propName });
-      xml += 
`${indent}<${elementName}>${applyEntities(propValue)}</${elementName}>\n`;
+    else if (typeof jsonPropValue !== "object") {
+      const elementName = applyInstanceNs({ ns, instanceNs, propName: 
jsonPropName });
+      xml += 
`${indent}<${elementName}>${applyEntities(jsonPropValue)}</${elementName}>\n`;
     }
     // array
-    else if (Array.isArray(propValue)) {
-      for (const item of propValue) {
-        const elementName = applyInstanceNs({ ns, instanceNs, propName: 
item?.["__$$element"] ?? propName });
+    else if (Array.isArray(jsonPropValue)) {
+      for (const item of jsonPropValue) {
+        const elementName = applyInstanceNs({ ns, instanceNs, propName: 
item?.["__$$element"] ?? jsonPropName });
         const { attrs, isEmpty, hasText } = buildAttrs(item);
         xml += `${indent}<${elementName}${attrs}`;
         if (isEmpty) {
@@ -474,7 +507,12 @@ export function build(args: {
           if (hasText) {
             xml += `>${applyEntities(item["__$$text"])}</${elementName}>\n`;
           } else {
-            xml += `>\n${build({ ...args, json: item, indent: `${indent}  ` 
})}`;
+            xml += `>\n${build({
+              ...args,
+              json: item,
+              metaType: getPropMetaTypeForJsonObj({ args, jsonObj: item, 
jsonPropName, metaType }),
+              indent: `${indent}  `,
+            })}`;
             xml += `${indent}</${elementName}>\n`;
           }
         }
@@ -482,8 +520,8 @@ export function build(args: {
     }
     // nested element
     else {
-      const item = propValue;
-      const elementName = applyInstanceNs({ ns, instanceNs, propName: 
item["__$$element"] ?? propName });
+      const item = jsonPropValue;
+      const elementName = applyInstanceNs({ ns, instanceNs, propName: 
item["__$$element"] ?? jsonPropName });
       const { attrs, isEmpty, hasText } = buildAttrs(item);
       xml += `${indent}<${elementName}${attrs}`;
       if (isEmpty) {
@@ -492,7 +530,12 @@ export function build(args: {
         if (hasText) {
           xml += `>${applyEntities(item["__$$text"])}</${elementName}>\n`;
         } else {
-          xml += `>\n${build({ ...args, json: item, indent: `${indent}  ` })}`;
+          xml += `>\n${build({
+            ...args,
+            json: item,
+            metaType: getPropMetaTypeForJsonObj({ args, jsonObj: item, 
jsonPropName, metaType }),
+            indent: `${indent}  `,
+          })}`;
           xml += `${indent}</${elementName}>\n`;
         }
       }
@@ -502,6 +545,26 @@ export function build(args: {
   return xml;
 }
 
+// To know what the metaType of `jsonObj` is, we first need to check if it is 
mapped as an element.
+// If it is, we use the type mapped to elements with its `__$$element` 
attribute or `jsonPropName`
+// If it's not, we proceed normally with traversing the metaType tree.
+function getPropMetaTypeForJsonObj({
+  args: { elements, meta },
+  jsonObj,
+  jsonPropName,
+  metaType,
+}: {
+  args: {
+    elements: Elements;
+    meta: Meta;
+  };
+  jsonObj: any;
+  jsonPropName: string;
+  metaType: MetaType | undefined;
+}): MetaType | undefined {
+  return meta[elements[jsonObj?.["__$$element"] ?? jsonPropName] ?? 
metaType?.[jsonPropName]?.type ?? ""];
+}
+
 function applyInstanceNs({
   propName,
   ns,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to