Skip to content

Commit 074fe75

Browse files
authored
Fixes Problem with DataTypeXsd parsing in xmlization (#458)
1 parent bd36316 commit 074fe75

4 files changed

Lines changed: 201 additions & 38 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "basyx-typescript-sdk",
3-
"version": "2.1.0",
3+
"version": "2.1.1",
44
"description": "BaSyx TypeScript SDK for developing applications and components for the Asset Administration Shell (AAS)",
55
"main": "bundle/index.cjs",
66
"module": "bundle/index.mjs",

src/lib/aas-dataformat-xml/xmlization.ts

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,29 @@ const ARRAY_ELEMENTS = new Set([
12141214
'operationVariable',
12151215
]);
12161216

1217+
function parseDataTypeDefXsd(dataType: unknown): DataTypeDefXsd | undefined {
1218+
if (dataType === undefined || dataType === null || dataType === '') {
1219+
return undefined;
1220+
}
1221+
1222+
if (typeof dataType !== 'string' || !dataType.startsWith('xs:')) {
1223+
throw new Error(`Invalid literal of DataTypeDefXsd: ${String(dataType)}`);
1224+
}
1225+
1226+
const result = jsonization.dataTypeDefXsdFromJsonable(dataType);
1227+
if (result.error !== null || result.value === null) {
1228+
const errorMessage =
1229+
typeof result.error === 'string'
1230+
? result.error
1231+
: typeof (result.error as any)?.message === 'string'
1232+
? (result.error as any).message
1233+
: `Invalid literal of DataTypeDefXsd: ${dataType}`;
1234+
throw new Error(errorMessage);
1235+
}
1236+
1237+
return result.value;
1238+
}
1239+
12171240
function parseAssetAdministrationShell(data: any): AssetAdministrationShell {
12181241
return new AssetAdministrationShell(
12191242
data.id,
@@ -1237,15 +1260,7 @@ function parseAssetAdministrationShell(data: any): AssetAdministrationShell {
12371260
}
12381261

12391262
function parseExtension(data: any): Extension {
1240-
// Map XML valueType string to DataTypeDefXsd enum
1241-
let valueType = data.valueType || undefined;
1242-
if (valueType && typeof valueType === 'string' && valueType.startsWith('xs:')) {
1243-
// Extract the type after 'xs:' prefix and map to enum
1244-
const typeValue = valueType.substring(3);
1245-
const capitalizedType = typeValue.charAt(0).toUpperCase() + typeValue.slice(1);
1246-
// Map string to DataTypeDefXsd enum value
1247-
valueType = (DataTypeDefXsd as any)[capitalizedType];
1248-
}
1263+
const valueType = parseDataTypeDefXsd(data.valueType);
12491264

12501265
return new Extension(
12511266
data.name,
@@ -1451,15 +1466,7 @@ function parseSubmodel(data: any): Submodel {
14511466
}
14521467

14531468
function parseQualifier(data: any): Qualifier {
1454-
// Map XML valueType string to DataTypeDefXsd enum
1455-
let valueType = data.valueType || undefined;
1456-
if (valueType && typeof valueType === 'string') {
1457-
// Convert string to enum value using jsonization (expects xs: prefix)
1458-
const result = jsonization.dataTypeDefXsdFromJsonable(valueType);
1459-
if (result.error === null && result.value !== null) {
1460-
valueType = result.value;
1461-
}
1462-
}
1469+
const valueType = parseDataTypeDefXsd(data.valueType);
14631470

14641471
return new Qualifier(
14651472
data.type,
@@ -1506,15 +1513,7 @@ function parseSubmodelElements(data: any): any[] {
15061513
}
15071514

15081515
function parseProperty(data: any): Property {
1509-
// Map XML valueType string to DataTypeDefXsd enum
1510-
let valueType = data.valueType || undefined;
1511-
if (valueType && typeof valueType === 'string') {
1512-
// Convert string to enum value using jsonization (expects xs: prefix)
1513-
const result = jsonization.dataTypeDefXsdFromJsonable(valueType);
1514-
if (result.error === null && result.value !== null) {
1515-
valueType = result.value;
1516-
}
1517-
}
1516+
const valueType = parseDataTypeDefXsd(data.valueType);
15181517

15191518
return new Property(
15201519
valueType as DataTypeDefXsd,
@@ -1565,15 +1564,7 @@ function parseMultiLanguageProperty(data: any): MultiLanguageProperty {
15651564
}
15661565

15671566
function parseRange(data: any): Range {
1568-
// Map XML valueType string to DataTypeDefXsd enum
1569-
let valueType = data.valueType || undefined;
1570-
if (valueType && typeof valueType === 'string') {
1571-
// Convert string to enum value using jsonization (expects xs: prefix)
1572-
const result = jsonization.dataTypeDefXsdFromJsonable(valueType);
1573-
if (result.error === null && result.value !== null) {
1574-
valueType = result.value;
1575-
}
1576-
}
1567+
const valueType = parseDataTypeDefXsd(data.valueType);
15771568

15781569
return new Range(
15791570
valueType as DataTypeDefXsd,
@@ -1799,7 +1790,7 @@ function parseSubmodelElementList(data: any): SubmodelElementList {
17991790
: undefined,
18001791
data.orderRelevant !== undefined ? data.orderRelevant === 'true' || data.orderRelevant === true : undefined,
18011792
data.semanticIdListElement ? parseReference(data.semanticIdListElement) : undefined,
1802-
data.valueTypeListElement || undefined,
1793+
parseDataTypeDefXsd(data.valueTypeListElement),
18031794
data.value ? parseSubmodelElements(data.value) : undefined
18041795
);
18051796
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<aas:environment xmlns:aas="https://admin-shell.io/aas/3/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://admin-shell.io/aas/3/1 AAS.xsd">
3+
<aas:assetAdministrationShells>
4+
<aas:assetAdministrationShell>
5+
<aas:idShort>System</aas:idShort>
6+
<aas:displayName>
7+
<aas:langStringNameType>
8+
<aas:language>en</aas:language>
9+
<aas:text>System AAS</aas:text>
10+
</aas:langStringNameType>
11+
</aas:displayName>
12+
<aas:id>4976b09f-04b6-4b6e-a16d-b209c7429c73</aas:id>
13+
<aas:assetInformation>
14+
<aas:assetKind>Instance</aas:assetKind>
15+
</aas:assetInformation>
16+
<aas:submodels>
17+
<aas:reference>
18+
<aas:type>ExternalReference</aas:type>
19+
<aas:keys>
20+
<aas:key>
21+
<aas:type>Submodel</aas:type>
22+
<aas:value>8b2d5ec5-d286-48a7-9680-32e61ae6ae22</aas:value>
23+
</aas:key>
24+
</aas:keys>
25+
</aas:reference>
26+
</aas:submodels>
27+
</aas:assetAdministrationShell>
28+
</aas:assetAdministrationShells>
29+
<aas:submodels>
30+
<aas:submodel>
31+
<aas:idShort>Security</aas:idShort>
32+
<aas:id>8b2d5ec5-d286-48a7-9680-32e61ae6ae22</aas:id>
33+
<aas:kind>Instance</aas:kind>
34+
<aas:submodelElements>
35+
<aas:submodelElementList>
36+
<aas:idShort>Users</aas:idShort>
37+
<aas:orderRelevant>false</aas:orderRelevant>
38+
<aas:typeValueListElement>SubmodelElement</aas:typeValueListElement>
39+
<aas:valueTypeListElement>xs:string</aas:valueTypeListElement>
40+
<aas:value>
41+
<aas:submodelElementCollection>
42+
<aas:value>
43+
<aas:property>
44+
<aas:idShort>UserName</aas:idShort>
45+
<aas:valueType>xs:string</aas:valueType>
46+
<aas:value>Hans Peter</aas:value>
47+
</aas:property>
48+
<aas:relationshipElement>
49+
<aas:idShort>UserGroup</aas:idShort>
50+
<aas:first>
51+
<aas:type>ModelReference</aas:type>
52+
<aas:keys>
53+
<aas:key>
54+
<aas:type>Submodel</aas:type>
55+
<aas:value>8b2d5ec5-d286-48a7-9680-32e61ae6ae22</aas:value>
56+
</aas:key>
57+
<aas:key>
58+
<aas:type>SubmodelElementList</aas:type>
59+
<aas:value>Users</aas:value>
60+
</aas:key>
61+
<aas:key>
62+
<aas:type>SubmodelElementCollection</aas:type>
63+
<aas:value>0</aas:value>
64+
</aas:key>
65+
</aas:keys>
66+
</aas:first>
67+
<aas:second>
68+
<aas:type>ModelReference</aas:type>
69+
<aas:keys>
70+
<aas:key>
71+
<aas:type>Submodel</aas:type>
72+
<aas:value>8b2d5ec5-d286-48a7-9680-32e61ae6ae22</aas:value>
73+
</aas:key>
74+
<aas:key>
75+
<aas:type>SubmodelElementList</aas:type>
76+
<aas:value>Groups</aas:value>
77+
</aas:key>
78+
<aas:key>
79+
<aas:type>SubmodelElementCollection</aas:type>
80+
<aas:value>0</aas:value>
81+
</aas:key>
82+
<aas:key>
83+
<aas:type>Property</aas:type>
84+
<aas:value>GroupName</aas:value>
85+
</aas:key>
86+
</aas:keys>
87+
</aas:second>
88+
</aas:relationshipElement>
89+
</aas:value>
90+
</aas:submodelElementCollection>
91+
</aas:value>
92+
</aas:submodelElementList>
93+
<aas:submodelElementList>
94+
<aas:idShort>Groups</aas:idShort>
95+
<aas:orderRelevant>false</aas:orderRelevant>
96+
<aas:typeValueListElement>SubmodelElement</aas:typeValueListElement>
97+
<aas:valueTypeListElement>xs:string</aas:valueTypeListElement>
98+
<aas:value>
99+
<aas:submodelElementCollection>
100+
<aas:value>
101+
<aas:property>
102+
<aas:idShort>GroupName</aas:idShort>
103+
<aas:valueType>xs:string</aas:valueType>
104+
<aas:value>Administrator</aas:value>
105+
</aas:property>
106+
</aas:value>
107+
</aas:submodelElementCollection>
108+
</aas:value>
109+
</aas:submodelElementList>
110+
</aas:submodelElements>
111+
</aas:submodel>
112+
</aas:submodels>
113+
</aas:environment>

src/unit-tests/lib/aas-dataformat-xml/xmlization.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,65 @@ describe('deserializeXml', () => {
650650
expect(submodel.kind).toBe(ModellingKind.Template);
651651
});
652652

653+
test('should deserialize list valueTypeListElement from test-lists.xml as DataTypeDefXsd enum', () => {
654+
const xmlPath = path.join(__dirname, 'xml-test-files/test-lists.xml');
655+
const xmlContent = fs.readFileSync(xmlPath, 'utf-8');
656+
657+
const result = deserializeXml(xmlContent);
658+
expect(result.submodels).toBeDefined();
659+
expect(result.submodels).toHaveLength(1);
660+
661+
const submodel = result.submodels![0];
662+
expect(submodel.submodelElements).toBeDefined();
663+
664+
const usersList = submodel.submodelElements!.find((el: any) => el.idShort === 'Users') as SubmodelElementList;
665+
const groupsList = submodel.submodelElements!.find((el: any) => el.idShort === 'Groups') as SubmodelElementList;
666+
667+
expect(usersList).toBeDefined();
668+
expect(groupsList).toBeDefined();
669+
expect(usersList.valueTypeListElement).toBe(DataTypeDefXsd.String);
670+
expect(groupsList.valueTypeListElement).toBe(DataTypeDefXsd.String);
671+
});
672+
673+
test('should throw on non-xs DataTypeDefXsd literals in valueTypeListElement', () => {
674+
const xmlContent = `<?xml version='1.0' encoding='UTF-8'?>
675+
<aas:environment xmlns:aas="https://admin-shell.io/aas/3/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://admin-shell.io/aas/3/1 AAS.xsd">
676+
<aas:submodels>
677+
<aas:submodel>
678+
<aas:id>sm-1</aas:id>
679+
<aas:submodelElements>
680+
<aas:submodelElementList>
681+
<aas:idShort>Users</aas:idShort>
682+
<aas:typeValueListElement>SubmodelElement</aas:typeValueListElement>
683+
<aas:valueTypeListElement>string</aas:valueTypeListElement>
684+
</aas:submodelElementList>
685+
</aas:submodelElements>
686+
</aas:submodel>
687+
</aas:submodels>
688+
</aas:environment>`;
689+
690+
expect(() => deserializeXml(xmlContent)).toThrow('Invalid literal of DataTypeDefXsd: string');
691+
});
692+
693+
test('should throw on unknown xs-prefixed DataTypeDefXsd literals', () => {
694+
const xmlContent = `<?xml version='1.0' encoding='UTF-8'?>
695+
<aas:environment xmlns:aas="https://admin-shell.io/aas/3/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://admin-shell.io/aas/3/1 AAS.xsd">
696+
<aas:submodels>
697+
<aas:submodel>
698+
<aas:id>sm-1</aas:id>
699+
<aas:submodelElements>
700+
<aas:property>
701+
<aas:idShort>Prop</aas:idShort>
702+
<aas:valueType>xs:notAType</aas:valueType>
703+
</aas:property>
704+
</aas:submodelElements>
705+
</aas:submodel>
706+
</aas:submodels>
707+
</aas:environment>`;
708+
709+
expect(() => deserializeXml(xmlContent)).toThrow(/DataTypeDefXsd: xs:notAType/);
710+
});
711+
653712
test('should deserialize XML with minimal Asset Administration Shell', () => {
654713
const xmlPath = path.join(__dirname, 'xml-test-files/test-minimal.xml');
655714
const xmlContent = fs.readFileSync(xmlPath, 'utf-8');

0 commit comments

Comments
 (0)