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>"New Decision"</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>"New Decision"</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>"New Decision"</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]