@azure-tools/typespec-ts 0.48.0 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +20 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/modular/buildClientContext.d.ts.map +1 -1
- package/dist/src/modular/buildClientContext.js +7 -3
- package/dist/src/modular/buildClientContext.js.map +1 -1
- package/dist/src/modular/buildOperations.d.ts.map +1 -1
- package/dist/src/modular/buildOperations.js +24 -3
- package/dist/src/modular/buildOperations.js.map +1 -1
- package/dist/src/modular/emitModels.d.ts.map +1 -1
- package/dist/src/modular/emitModels.js +19 -0
- package/dist/src/modular/emitModels.js.map +1 -1
- package/dist/src/modular/emitSamples.js +2 -2
- package/dist/src/modular/emitSamples.js.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.d.ts +2 -1
- package/dist/src/modular/helpers/clientHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/clientHelpers.js +5 -2
- package/dist/src/modular/helpers/clientHelpers.js.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.d.ts.map +1 -1
- package/dist/src/modular/helpers/operationHelpers.js +266 -43
- package/dist/src/modular/helpers/operationHelpers.js.map +1 -1
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts +32 -0
- package/dist/src/modular/serialization/buildXmlSerializerFunction.d.ts.map +1 -0
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js +373 -0
- package/dist/src/modular/serialization/buildXmlSerializerFunction.js.map +1 -0
- package/dist/src/modular/static-helpers-metadata.d.ts +57 -0
- package/dist/src/modular/static-helpers-metadata.d.ts.map +1 -1
- package/dist/src/modular/static-helpers-metadata.js +57 -0
- package/dist/src/modular/static-helpers-metadata.js.map +1 -1
- package/dist/src/utils/clientUtils.d.ts.map +1 -1
- package/dist/src/utils/clientUtils.js +1 -0
- package/dist/src/utils/clientUtils.js.map +1 -1
- package/dist/src/utils/mediaTypes.d.ts +4 -0
- package/dist/src/utils/mediaTypes.d.ts.map +1 -1
- package/dist/src/utils/mediaTypes.js +10 -0
- package/dist/src/utils/mediaTypes.js.map +1 -1
- package/dist/src/utils/modelUtils.d.ts.map +1 -1
- package/dist/src/utils/modelUtils.js +3 -0
- package/dist/src/utils/modelUtils.js.map +1 -1
- package/dist/src/utils/operationUtil.d.ts +12 -0
- package/dist/src/utils/operationUtil.d.ts.map +1 -1
- package/dist/src/utils/operationUtil.js +22 -1
- package/dist/src/utils/operationUtil.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/index.ts +20 -5
- package/src/modular/buildClientContext.ts +12 -5
- package/src/modular/buildOperations.ts +34 -3
- package/src/modular/emitModels.ts +43 -0
- package/src/modular/emitSamples.ts +2 -2
- package/src/modular/helpers/clientHelpers.ts +6 -2
- package/src/modular/helpers/operationHelpers.ts +377 -57
- package/src/modular/serialization/buildXmlSerializerFunction.ts +511 -0
- package/src/modular/static-helpers-metadata.ts +58 -0
- package/src/utils/clientUtils.ts +1 -0
- package/src/utils/mediaTypes.ts +12 -0
- package/src/utils/modelUtils.ts +3 -0
- package/src/utils/operationUtil.ts +34 -1
- package/static/static-helpers/serialization/xml-helpers.ts +596 -0
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
import { FunctionDeclarationStructure, StructureKind } from "ts-morph";
|
|
5
|
+
import {
|
|
6
|
+
SdkModelPropertyType,
|
|
7
|
+
SdkModelType,
|
|
8
|
+
SdkPackage,
|
|
9
|
+
SdkType,
|
|
10
|
+
UsageFlags
|
|
11
|
+
} from "@azure-tools/typespec-client-generator-core";
|
|
12
|
+
import { SdkContext } from "../../utils/interfaces.js";
|
|
13
|
+
import {
|
|
14
|
+
getAllAncestors,
|
|
15
|
+
getAllProperties
|
|
16
|
+
} from "../helpers/operationHelpers.js";
|
|
17
|
+
import { normalizeModelName } from "../emitModels.js";
|
|
18
|
+
import { NameType } from "@azure-tools/rlc-common";
|
|
19
|
+
import { isAzureCoreErrorType } from "../../utils/modelUtils.js";
|
|
20
|
+
import {
|
|
21
|
+
isSupportedSerializeType,
|
|
22
|
+
ModelSerializeOptions
|
|
23
|
+
} from "./serializeUtils.js";
|
|
24
|
+
import { XmlHelpers } from "../static-helpers-metadata.js";
|
|
25
|
+
import { resolveReference } from "../../framework/reference.js";
|
|
26
|
+
import { refkey } from "../../framework/refkey.js";
|
|
27
|
+
import { reportDiagnostic } from "../../lib.js";
|
|
28
|
+
import { NoTarget } from "@typespec/compiler";
|
|
29
|
+
import { isMetadata } from "@typespec/http";
|
|
30
|
+
import { normalizeModelPropertyName } from "../type-expressions/get-type-expression.js";
|
|
31
|
+
import { isReadOnly } from "@azure-tools/typespec-client-generator-core";
|
|
32
|
+
import { buildModelSerializer } from "./buildSerializerFunction.js";
|
|
33
|
+
import { buildModelDeserializer } from "./buildDeserializerFunction.js";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Checks if a model type has XML serialization options defined
|
|
37
|
+
*/
|
|
38
|
+
export function hasXmlSerialization(type: SdkType): boolean {
|
|
39
|
+
if (type.kind !== "model") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
// Check if the model itself has XML options
|
|
43
|
+
if (type.serializationOptions?.xml) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
// Check if any property has XML options
|
|
47
|
+
return type.properties?.some((p) => p.serializationOptions?.xml) ?? false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if any model in the SDK package uses XML serialization
|
|
52
|
+
*/
|
|
53
|
+
export function packageUsesXmlSerialization(
|
|
54
|
+
sdkPackage: SdkPackage<any>
|
|
55
|
+
): boolean {
|
|
56
|
+
for (const model of sdkPackage.models) {
|
|
57
|
+
if (hasXmlSerialization(model)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Gets the XML element name for a model type
|
|
66
|
+
*/
|
|
67
|
+
export function getXmlRootName(type: SdkModelType): string {
|
|
68
|
+
return type.serializationOptions?.xml?.name ?? type.name;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets the XML namespace for a model type
|
|
73
|
+
*/
|
|
74
|
+
export function getXmlRootNs(
|
|
75
|
+
type: SdkModelType
|
|
76
|
+
): { namespace: string; prefix: string } | undefined {
|
|
77
|
+
return type.serializationOptions?.xml?.ns;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Builds an XML serializer function for a model type
|
|
82
|
+
*/
|
|
83
|
+
export function buildXmlModelSerializer(
|
|
84
|
+
context: SdkContext,
|
|
85
|
+
type: SdkModelType,
|
|
86
|
+
options: ModelSerializeOptions = {
|
|
87
|
+
nameOnly: false,
|
|
88
|
+
skipDiscriminatedUnionSuffix: false
|
|
89
|
+
}
|
|
90
|
+
): FunctionDeclarationStructure | string | undefined {
|
|
91
|
+
if (!isSupportedSerializeType(type)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!type.name) {
|
|
96
|
+
reportDiagnostic(context.program, {
|
|
97
|
+
code: "anonymous-type-serialization",
|
|
98
|
+
target: type.__raw || NoTarget
|
|
99
|
+
});
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
!type.usage ||
|
|
105
|
+
(type.usage !== undefined &&
|
|
106
|
+
(type.usage & UsageFlags.Input) !== UsageFlags.Input)
|
|
107
|
+
) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (isAzureCoreErrorType(context.program, type.__raw!)) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const serializerFunctionName =
|
|
116
|
+
options.predefinedName ??
|
|
117
|
+
`${normalizeModelName(
|
|
118
|
+
context,
|
|
119
|
+
type,
|
|
120
|
+
NameType.Operation,
|
|
121
|
+
options.skipDiscriminatedUnionSuffix
|
|
122
|
+
)}XmlSerializer`;
|
|
123
|
+
|
|
124
|
+
if (options.nameOnly) {
|
|
125
|
+
return resolveReference(refkey(type, "xmlSerializer"));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const serializeToXmlRef = resolveReference(XmlHelpers.serializeToXml);
|
|
129
|
+
const xmlPropertyMetadataRef = resolveReference(
|
|
130
|
+
XmlHelpers.XmlPropertyMetadata
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const properties = getAllProperties(context, type, getAllAncestors(type));
|
|
134
|
+
const xmlRootName = getXmlRootName(type);
|
|
135
|
+
const xmlRootNs = getXmlRootNs(type);
|
|
136
|
+
|
|
137
|
+
// Build property metadata array
|
|
138
|
+
const propertyMetadata = buildPropertyMetadataArray(context, properties);
|
|
139
|
+
|
|
140
|
+
const statements: string[] = [];
|
|
141
|
+
|
|
142
|
+
// Generate the properties metadata constant
|
|
143
|
+
statements.push(
|
|
144
|
+
`const properties: ${xmlPropertyMetadataRef}[] = [${propertyMetadata}];`
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Generate the serialization call
|
|
148
|
+
if (xmlRootNs) {
|
|
149
|
+
statements.push(
|
|
150
|
+
`return ${serializeToXmlRef}(item, properties, "${xmlRootName}", { namespace: "${xmlRootNs.namespace}", prefix: "${xmlRootNs.prefix}" });`
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
statements.push(
|
|
154
|
+
`return ${serializeToXmlRef}(item, properties, "${xmlRootName}");`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const serializerFunction: FunctionDeclarationStructure = {
|
|
159
|
+
kind: StructureKind.Function,
|
|
160
|
+
name: serializerFunctionName,
|
|
161
|
+
isExported: true,
|
|
162
|
+
parameters: [
|
|
163
|
+
{
|
|
164
|
+
name: "item",
|
|
165
|
+
type: resolveReference(refkey(type))
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
returnType: "string",
|
|
169
|
+
statements
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return serializerFunction;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Builds the property metadata array for XML serialization
|
|
177
|
+
*/
|
|
178
|
+
function buildPropertyMetadataArray(
|
|
179
|
+
context: SdkContext,
|
|
180
|
+
properties: SdkModelPropertyType[]
|
|
181
|
+
): string {
|
|
182
|
+
const metadataEntries: string[] = [];
|
|
183
|
+
|
|
184
|
+
for (const property of properties) {
|
|
185
|
+
if (property.kind !== "property") {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (isReadOnly(property)) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (isMetadata(context.program, property.__raw!)) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const xmlOptions = property.serializationOptions?.xml;
|
|
196
|
+
const jsonOptions = property.serializationOptions?.json;
|
|
197
|
+
const propertyName = normalizeModelPropertyName(context, property);
|
|
198
|
+
const cleanPropertyName = propertyName.replace(/^"|"$/g, "");
|
|
199
|
+
|
|
200
|
+
// Use XML name if available, fall back to JSON name, then property name
|
|
201
|
+
const serializedName =
|
|
202
|
+
xmlOptions?.name ?? jsonOptions?.name ?? property.name;
|
|
203
|
+
|
|
204
|
+
const metadataObj: string[] = [
|
|
205
|
+
`propertyName: "${cleanPropertyName}"`,
|
|
206
|
+
`xmlOptions: { name: "${serializedName}"${buildXmlOptionsString(xmlOptions)} }`
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
// Add type information for special handling
|
|
210
|
+
const typeInfo = getPropertyTypeInfo(property.type);
|
|
211
|
+
if (typeInfo.type) {
|
|
212
|
+
metadataObj.push(`type: "${typeInfo.type}"`);
|
|
213
|
+
}
|
|
214
|
+
if (typeInfo.dateEncoding) {
|
|
215
|
+
metadataObj.push(`dateEncoding: "${typeInfo.dateEncoding}"`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Add serializer for complex types
|
|
219
|
+
const nestedSerializer = getNestedXmlSerializer(context, property.type);
|
|
220
|
+
if (nestedSerializer) {
|
|
221
|
+
metadataObj.push(`serializer: ${nestedSerializer}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
metadataEntries.push(`{ ${metadataObj.join(", ")} }`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return metadataEntries.join(",\n ");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Builds the XML options string from XmlSerializationOptions
|
|
232
|
+
*/
|
|
233
|
+
function buildXmlOptionsString(xmlOptions?: {
|
|
234
|
+
name: string;
|
|
235
|
+
attribute?: boolean;
|
|
236
|
+
ns?: { namespace: string; prefix: string };
|
|
237
|
+
unwrapped?: boolean;
|
|
238
|
+
itemsName?: string;
|
|
239
|
+
itemsNs?: { namespace: string; prefix: string };
|
|
240
|
+
}): string {
|
|
241
|
+
if (!xmlOptions) {
|
|
242
|
+
return "";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const parts: string[] = [];
|
|
246
|
+
|
|
247
|
+
if (xmlOptions.attribute) {
|
|
248
|
+
parts.push(`attribute: true`);
|
|
249
|
+
}
|
|
250
|
+
if (xmlOptions.ns) {
|
|
251
|
+
parts.push(
|
|
252
|
+
`ns: { namespace: "${xmlOptions.ns.namespace}", prefix: "${xmlOptions.ns.prefix}" }`
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (xmlOptions.unwrapped) {
|
|
256
|
+
parts.push(`unwrapped: true`);
|
|
257
|
+
}
|
|
258
|
+
if (xmlOptions.itemsName) {
|
|
259
|
+
parts.push(`itemsName: "${xmlOptions.itemsName}"`);
|
|
260
|
+
}
|
|
261
|
+
if (xmlOptions.itemsNs) {
|
|
262
|
+
parts.push(
|
|
263
|
+
`itemsNs: { namespace: "${xmlOptions.itemsNs.namespace}", prefix: "${xmlOptions.itemsNs.prefix}" }`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return parts.length > 0 ? `, ${parts.join(", ")}` : "";
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Gets type information for a property for XML serialization
|
|
272
|
+
*/
|
|
273
|
+
function getPropertyTypeInfo(type: SdkType): {
|
|
274
|
+
type?: "array" | "object" | "primitive" | "date" | "bytes" | "dict";
|
|
275
|
+
dateEncoding?: "rfc3339" | "rfc7231" | "unixTimestamp";
|
|
276
|
+
} {
|
|
277
|
+
switch (type.kind) {
|
|
278
|
+
case "array":
|
|
279
|
+
return { type: "array" };
|
|
280
|
+
case "model":
|
|
281
|
+
return { type: "object" };
|
|
282
|
+
case "dict":
|
|
283
|
+
return { type: "dict" };
|
|
284
|
+
case "utcDateTime":
|
|
285
|
+
return {
|
|
286
|
+
type: "date",
|
|
287
|
+
dateEncoding:
|
|
288
|
+
(type.encode as "rfc3339" | "rfc7231" | "unixTimestamp") ?? "rfc3339"
|
|
289
|
+
};
|
|
290
|
+
case "bytes":
|
|
291
|
+
return { type: "bytes" };
|
|
292
|
+
default:
|
|
293
|
+
return { type: "primitive" };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Gets the nested XML serializer function reference for complex types
|
|
299
|
+
*/
|
|
300
|
+
function getNestedXmlSerializer(
|
|
301
|
+
context: SdkContext,
|
|
302
|
+
type: SdkType
|
|
303
|
+
): string | undefined {
|
|
304
|
+
if (type.kind === "model") {
|
|
305
|
+
// For nested objects, use the regular JSON serializer which returns objects
|
|
306
|
+
// The XML builder will convert these objects to XML elements
|
|
307
|
+
const serializerName = buildModelSerializer(context, type, {
|
|
308
|
+
nameOnly: true,
|
|
309
|
+
skipDiscriminatedUnionSuffix: false
|
|
310
|
+
});
|
|
311
|
+
if (typeof serializerName === "string") {
|
|
312
|
+
return serializerName;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (type.kind === "array" && type.valueType.kind === "model") {
|
|
316
|
+
// For arrays, use the regular JSON serializer which returns objects
|
|
317
|
+
// The XML helper calls this for each item and handles the array mapping
|
|
318
|
+
const itemSerializer = buildModelSerializer(context, type.valueType, {
|
|
319
|
+
nameOnly: true,
|
|
320
|
+
skipDiscriminatedUnionSuffix: false
|
|
321
|
+
});
|
|
322
|
+
if (typeof itemSerializer === "string") {
|
|
323
|
+
return itemSerializer;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Builds an XML deserializer function for a model type
|
|
331
|
+
*/
|
|
332
|
+
export function buildXmlModelDeserializer(
|
|
333
|
+
context: SdkContext,
|
|
334
|
+
type: SdkModelType,
|
|
335
|
+
options: ModelSerializeOptions = {
|
|
336
|
+
nameOnly: false,
|
|
337
|
+
skipDiscriminatedUnionSuffix: false
|
|
338
|
+
}
|
|
339
|
+
): FunctionDeclarationStructure | string | undefined {
|
|
340
|
+
if (!isSupportedSerializeType(type)) {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!type.name) {
|
|
345
|
+
reportDiagnostic(context.program, {
|
|
346
|
+
code: "anonymous-type-deserialization",
|
|
347
|
+
target: type.__raw || NoTarget
|
|
348
|
+
});
|
|
349
|
+
return undefined;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (
|
|
353
|
+
!type.usage ||
|
|
354
|
+
(type.usage !== undefined &&
|
|
355
|
+
(type.usage & UsageFlags.Output) !== UsageFlags.Output &&
|
|
356
|
+
(type.usage & UsageFlags.Exception) !== UsageFlags.Exception)
|
|
357
|
+
) {
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (isAzureCoreErrorType(context.program, type.__raw!)) {
|
|
362
|
+
return undefined;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const deserializerFunctionName =
|
|
366
|
+
options.predefinedName ??
|
|
367
|
+
`${normalizeModelName(
|
|
368
|
+
context,
|
|
369
|
+
type,
|
|
370
|
+
NameType.Operation,
|
|
371
|
+
options.skipDiscriminatedUnionSuffix
|
|
372
|
+
)}XmlDeserializer`;
|
|
373
|
+
|
|
374
|
+
if (options.nameOnly) {
|
|
375
|
+
return resolveReference(refkey(type, "xmlDeserializer"));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const deserializeFromXmlRef = resolveReference(XmlHelpers.deserializeFromXml);
|
|
379
|
+
const xmlPropertyDeserializeMetadataRef = resolveReference(
|
|
380
|
+
XmlHelpers.XmlPropertyDeserializeMetadata
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const properties = getAllProperties(context, type, getAllAncestors(type));
|
|
384
|
+
const xmlRootName = getXmlRootName(type);
|
|
385
|
+
const xmlRootNs = getXmlRootNs(type);
|
|
386
|
+
|
|
387
|
+
// Build property metadata array for deserialization
|
|
388
|
+
const propertyMetadata = buildDeserializePropertyMetadataArray(
|
|
389
|
+
context,
|
|
390
|
+
properties
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
const statements: string[] = [];
|
|
394
|
+
|
|
395
|
+
// Generate the properties metadata constant
|
|
396
|
+
statements.push(
|
|
397
|
+
`const properties: ${xmlPropertyDeserializeMetadataRef}[] = [${propertyMetadata}];`
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// Generate the deserialization call
|
|
401
|
+
const typeRef = resolveReference(refkey(type));
|
|
402
|
+
if (xmlRootNs) {
|
|
403
|
+
statements.push(
|
|
404
|
+
`return ${deserializeFromXmlRef}<${typeRef}>(xmlString, properties, "${xmlRootName}", { namespace: "${xmlRootNs.namespace}", prefix: "${xmlRootNs.prefix}" });`
|
|
405
|
+
);
|
|
406
|
+
} else {
|
|
407
|
+
statements.push(
|
|
408
|
+
`return ${deserializeFromXmlRef}<${typeRef}>(xmlString, properties, "${xmlRootName}");`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const deserializerFunction: FunctionDeclarationStructure = {
|
|
413
|
+
kind: StructureKind.Function,
|
|
414
|
+
name: deserializerFunctionName,
|
|
415
|
+
isExported: true,
|
|
416
|
+
parameters: [
|
|
417
|
+
{
|
|
418
|
+
name: "xmlString",
|
|
419
|
+
type: "string"
|
|
420
|
+
}
|
|
421
|
+
],
|
|
422
|
+
returnType: resolveReference(refkey(type)),
|
|
423
|
+
statements
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
return deserializerFunction;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Builds the property metadata array for XML deserialization
|
|
431
|
+
*/
|
|
432
|
+
function buildDeserializePropertyMetadataArray(
|
|
433
|
+
context: SdkContext,
|
|
434
|
+
properties: SdkModelPropertyType[]
|
|
435
|
+
): string {
|
|
436
|
+
const metadataEntries: string[] = [];
|
|
437
|
+
|
|
438
|
+
for (const property of properties) {
|
|
439
|
+
if (property.kind !== "property") {
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
if (isMetadata(context.program, property.__raw!)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const xmlOptions = property.serializationOptions?.xml;
|
|
447
|
+
const jsonOptions = property.serializationOptions?.json;
|
|
448
|
+
const propertyName = normalizeModelPropertyName(context, property);
|
|
449
|
+
const cleanPropertyName = propertyName.replace(/^"|"$/g, "");
|
|
450
|
+
|
|
451
|
+
// Use XML name if available, fall back to JSON name, then property name
|
|
452
|
+
const serializedName =
|
|
453
|
+
xmlOptions?.name ?? jsonOptions?.name ?? property.name;
|
|
454
|
+
|
|
455
|
+
const metadataObj: string[] = [
|
|
456
|
+
`propertyName: "${cleanPropertyName}"`,
|
|
457
|
+
`xmlOptions: { name: "${serializedName}"${buildXmlOptionsString(xmlOptions)} }`
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
// Add type information for special handling
|
|
461
|
+
const typeInfo = getPropertyTypeInfo(property.type);
|
|
462
|
+
if (typeInfo.type) {
|
|
463
|
+
metadataObj.push(`type: "${typeInfo.type}"`);
|
|
464
|
+
}
|
|
465
|
+
if (typeInfo.dateEncoding) {
|
|
466
|
+
metadataObj.push(`dateEncoding: "${typeInfo.dateEncoding}"`);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Add deserializer for complex types
|
|
470
|
+
const nestedDeserializer = getNestedXmlDeserializer(context, property.type);
|
|
471
|
+
if (nestedDeserializer) {
|
|
472
|
+
metadataObj.push(`deserializer: ${nestedDeserializer}`);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
metadataEntries.push(`{ ${metadataObj.join(", ")} }`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return metadataEntries.join(",\n ");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Gets the nested XML deserializer function reference for complex types
|
|
483
|
+
*/
|
|
484
|
+
function getNestedXmlDeserializer(
|
|
485
|
+
context: SdkContext,
|
|
486
|
+
type: SdkType
|
|
487
|
+
): string | undefined {
|
|
488
|
+
if (type.kind === "model") {
|
|
489
|
+
// For nested objects, use the regular JSON deserializer which takes parsed objects
|
|
490
|
+
// The XML parser has already converted the XML to an object structure
|
|
491
|
+
const deserializerName = buildModelDeserializer(context, type, {
|
|
492
|
+
nameOnly: true,
|
|
493
|
+
skipDiscriminatedUnionSuffix: false
|
|
494
|
+
});
|
|
495
|
+
if (typeof deserializerName === "string") {
|
|
496
|
+
return deserializerName;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
if (type.kind === "array" && type.valueType.kind === "model") {
|
|
500
|
+
// For arrays, use the regular JSON deserializer which takes parsed objects
|
|
501
|
+
// The XML helper calls this for each item and handles the array mapping
|
|
502
|
+
const itemDeserializer = buildModelDeserializer(context, type.valueType, {
|
|
503
|
+
nameOnly: true,
|
|
504
|
+
skipDiscriminatedUnionSuffix: false
|
|
505
|
+
});
|
|
506
|
+
if (typeof itemDeserializer === "string") {
|
|
507
|
+
return itemDeserializer;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return undefined;
|
|
511
|
+
}
|
|
@@ -162,3 +162,61 @@ export const CloudSettingHelpers = {
|
|
|
162
162
|
location: "cloudSettingHelpers.ts"
|
|
163
163
|
}
|
|
164
164
|
} as const;
|
|
165
|
+
|
|
166
|
+
export const XmlHelpers = {
|
|
167
|
+
XmlSerializationOptions: {
|
|
168
|
+
kind: "interface",
|
|
169
|
+
name: "XmlSerializationOptions",
|
|
170
|
+
location: "serialization/xml-helpers.ts"
|
|
171
|
+
},
|
|
172
|
+
XmlPropertyMetadata: {
|
|
173
|
+
kind: "interface",
|
|
174
|
+
name: "XmlPropertyMetadata",
|
|
175
|
+
location: "serialization/xml-helpers.ts"
|
|
176
|
+
},
|
|
177
|
+
XmlPropertyDeserializeMetadata: {
|
|
178
|
+
kind: "interface",
|
|
179
|
+
name: "XmlPropertyDeserializeMetadata",
|
|
180
|
+
location: "serialization/xml-helpers.ts"
|
|
181
|
+
},
|
|
182
|
+
serializeModelToXml: {
|
|
183
|
+
kind: "function",
|
|
184
|
+
name: "serializeModelToXml",
|
|
185
|
+
location: "serialization/xml-helpers.ts"
|
|
186
|
+
},
|
|
187
|
+
serializeToXml: {
|
|
188
|
+
kind: "function",
|
|
189
|
+
name: "serializeToXml",
|
|
190
|
+
location: "serialization/xml-helpers.ts"
|
|
191
|
+
},
|
|
192
|
+
xmlObjectToString: {
|
|
193
|
+
kind: "function",
|
|
194
|
+
name: "xmlObjectToString",
|
|
195
|
+
location: "serialization/xml-helpers.ts"
|
|
196
|
+
},
|
|
197
|
+
parseXmlString: {
|
|
198
|
+
kind: "function",
|
|
199
|
+
name: "parseXmlString",
|
|
200
|
+
location: "serialization/xml-helpers.ts"
|
|
201
|
+
},
|
|
202
|
+
deserializeXmlToModel: {
|
|
203
|
+
kind: "function",
|
|
204
|
+
name: "deserializeXmlToModel",
|
|
205
|
+
location: "serialization/xml-helpers.ts"
|
|
206
|
+
},
|
|
207
|
+
deserializeFromXml: {
|
|
208
|
+
kind: "function",
|
|
209
|
+
name: "deserializeFromXml",
|
|
210
|
+
location: "serialization/xml-helpers.ts"
|
|
211
|
+
},
|
|
212
|
+
isXmlContentType: {
|
|
213
|
+
kind: "function",
|
|
214
|
+
name: "isXmlContentType",
|
|
215
|
+
location: "serialization/xml-helpers.ts"
|
|
216
|
+
},
|
|
217
|
+
isJsonContentType: {
|
|
218
|
+
kind: "function",
|
|
219
|
+
name: "isJsonContentType",
|
|
220
|
+
location: "serialization/xml-helpers.ts"
|
|
221
|
+
}
|
|
222
|
+
} as const;
|
package/src/utils/clientUtils.ts
CHANGED
package/src/utils/mediaTypes.ts
CHANGED
|
@@ -142,6 +142,18 @@ export function isMediaTypeMultipartFormData(
|
|
|
142
142
|
return mt ? mt.type === multipart && mt.subtype === formData : false;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
/**
|
|
146
|
+
* Checks if the media type is any multipart type (multipart/mixed, multipart/form-data, etc.)
|
|
147
|
+
*/
|
|
148
|
+
export function isMediaTypeMultipart(mediaType: string | string[]): boolean {
|
|
149
|
+
if (Array.isArray(mediaType)) {
|
|
150
|
+
return mediaType.some((mt) => isMediaTypeMultipart(mt));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const mt = parseMediaType(mediaType);
|
|
154
|
+
return mt ? mt.type === multipart : false;
|
|
155
|
+
}
|
|
156
|
+
|
|
145
157
|
export function hasMediaType(
|
|
146
158
|
target: KnownMediaType,
|
|
147
159
|
sourceTypes: KnownMediaType[] = []
|
package/src/utils/modelUtils.ts
CHANGED
|
@@ -268,6 +268,9 @@ export function getSchemaForType(
|
|
|
268
268
|
if (isNullType(type)) {
|
|
269
269
|
return { name: "null", type: "null" };
|
|
270
270
|
}
|
|
271
|
+
if (type.kind === "Intrinsic" && type.name === "void") {
|
|
272
|
+
return { name: "void", type: "void" };
|
|
273
|
+
}
|
|
271
274
|
reportDiagnostic(program, {
|
|
272
275
|
code: "invalid-schema",
|
|
273
276
|
format: {
|
|
@@ -43,7 +43,12 @@ import {
|
|
|
43
43
|
HttpStatusCodesEntry
|
|
44
44
|
} from "@typespec/http";
|
|
45
45
|
import { SdkContext } from "./interfaces.js";
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
KnownMediaType,
|
|
48
|
+
knownMediaType,
|
|
49
|
+
isMediaTypeXml,
|
|
50
|
+
isMediaTypeMultipart
|
|
51
|
+
} from "./mediaTypes.js";
|
|
47
52
|
import { isByteOrByteUnion } from "./modelUtils.js";
|
|
48
53
|
import { getOperationNamespaceInterfaceName } from "./namespaceUtils.js";
|
|
49
54
|
import { resolveReference } from "../framework/reference.js";
|
|
@@ -200,6 +205,34 @@ export function isBinaryPayload(
|
|
|
200
205
|
return false;
|
|
201
206
|
}
|
|
202
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Checks if the content type(s) indicate XML payload
|
|
210
|
+
*/
|
|
211
|
+
export function isXmlPayload(contentType: string | string[]): boolean {
|
|
212
|
+
const contentTypes = Array.isArray(contentType) ? contentType : [contentType];
|
|
213
|
+
return contentTypes.some((ct) => isMediaTypeXml(ct));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Checks if the content type(s) indicate multipart payload (multipart/mixed, multipart/form-data, etc.)
|
|
218
|
+
*/
|
|
219
|
+
export function isMultipartPayload(contentType: string | string[]): boolean {
|
|
220
|
+
return isMediaTypeMultipart(contentType);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Checks if the operation supports multiple content types (e.g., both JSON and XML)
|
|
225
|
+
*/
|
|
226
|
+
export function hasDualFormatSupport(contentTypes: string[]): boolean {
|
|
227
|
+
const hasJson = contentTypes.some(
|
|
228
|
+
(ct) => knownMediaType(ct) === KnownMediaType.Json
|
|
229
|
+
);
|
|
230
|
+
const hasXml = contentTypes.some(
|
|
231
|
+
(ct) => knownMediaType(ct) === KnownMediaType.Xml
|
|
232
|
+
);
|
|
233
|
+
return hasJson && hasXml;
|
|
234
|
+
}
|
|
235
|
+
|
|
203
236
|
export function isLongRunningOperation(
|
|
204
237
|
program: Program,
|
|
205
238
|
operation: HttpOperation
|