in sdk/core/core-client/src/serializer.ts [857:1051]
function deserializeCompositeType(
serializer: Serializer,
mapper: CompositeMapper,
responseBody: any,
objectName: string,
options: RequiredSerializerOptions,
): any {
const xmlCharKey = options.xml.xmlCharKey ?? XML_CHARKEY;
if (getPolymorphicDiscriminatorRecursively(serializer, mapper)) {
mapper = getPolymorphicMapper(serializer, mapper, responseBody, "serializedName");
}
const modelProps = resolveModelProperties(serializer, mapper, objectName);
let instance: { [key: string]: any } = {};
const handledPropertyNames: string[] = [];
for (const key of Object.keys(modelProps)) {
const propertyMapper = modelProps[key];
const paths = splitSerializeName(modelProps[key].serializedName!);
handledPropertyNames.push(paths[0]);
const { serializedName, xmlName, xmlElementName } = propertyMapper;
let propertyObjectName = objectName;
if (serializedName !== "" && serializedName !== undefined) {
propertyObjectName = objectName + "." + serializedName;
}
const headerCollectionPrefix = (propertyMapper as DictionaryMapper).headerCollectionPrefix;
if (headerCollectionPrefix) {
const dictionary: any = {};
for (const headerKey of Object.keys(responseBody)) {
if (headerKey.startsWith(headerCollectionPrefix)) {
dictionary[headerKey.substring(headerCollectionPrefix.length)] = serializer.deserialize(
(propertyMapper as DictionaryMapper).type.value,
responseBody[headerKey],
propertyObjectName,
options,
);
}
handledPropertyNames.push(headerKey);
}
instance[key] = dictionary;
} else if (serializer.isXML) {
if (propertyMapper.xmlIsAttribute && responseBody[XML_ATTRKEY]) {
instance[key] = serializer.deserialize(
propertyMapper,
responseBody[XML_ATTRKEY][xmlName!],
propertyObjectName,
options,
);
} else if (propertyMapper.xmlIsMsText) {
if (responseBody[xmlCharKey] !== undefined) {
instance[key] = responseBody[xmlCharKey];
} else if (typeof responseBody === "string") {
// The special case where xml parser parses "<Name>content</Name>" into JSON of
// `{ name: "content"}` instead of `{ name: { "_": "content" }}`
instance[key] = responseBody;
}
} else {
const propertyName = xmlElementName || xmlName || serializedName;
if (propertyMapper.xmlIsWrapped) {
/* a list of <xmlElementName> wrapped by <xmlName>
For the xml example below
<Cors>
<CorsRule>...</CorsRule>
<CorsRule>...</CorsRule>
</Cors>
the responseBody has
{
Cors: {
CorsRule: [{...}, {...}]
}
}
xmlName is "Cors" and xmlElementName is"CorsRule".
*/
const wrapped = responseBody[xmlName!];
const elementList = wrapped?.[xmlElementName!] ?? [];
instance[key] = serializer.deserialize(
propertyMapper,
elementList,
propertyObjectName,
options,
);
handledPropertyNames.push(xmlName!);
} else {
const property = responseBody[propertyName!];
instance[key] = serializer.deserialize(
propertyMapper,
property,
propertyObjectName,
options,
);
handledPropertyNames.push(propertyName!);
}
}
} else {
// deserialize the property if it is present in the provided responseBody instance
let propertyInstance;
let res = responseBody;
// traversing the object step by step.
let steps = 0;
for (const item of paths) {
if (!res) break;
steps++;
res = res[item];
}
// only accept null when reaching the last position of object otherwise it would be undefined
if (res === null && steps < paths.length) {
res = undefined;
}
propertyInstance = res;
const polymorphicDiscriminator = mapper.type.polymorphicDiscriminator;
// checking that the model property name (key)(ex: "fishtype") and the
// clientName of the polymorphicDiscriminator {metadata} (ex: "fishtype")
// instead of the serializedName of the polymorphicDiscriminator (ex: "fish.type")
// is a better approach. The generator is not consistent with escaping '\.' in the
// serializedName of the property (ex: "fish\.type") that is marked as polymorphic discriminator
// and the serializedName of the metadata polymorphicDiscriminator (ex: "fish.type"). However,
// the clientName transformation of the polymorphicDiscriminator (ex: "fishtype") and
// the transformation of model property name (ex: "fishtype") is done consistently.
// Hence, it is a safer bet to rely on the clientName of the polymorphicDiscriminator.
if (
polymorphicDiscriminator &&
key === polymorphicDiscriminator.clientName &&
(propertyInstance === undefined || propertyInstance === null)
) {
propertyInstance = mapper.serializedName;
}
let serializedValue;
// paging
if (Array.isArray(responseBody[key]) && modelProps[key].serializedName === "") {
propertyInstance = responseBody[key];
const arrayInstance = serializer.deserialize(
propertyMapper,
propertyInstance,
propertyObjectName,
options,
);
// Copy over any properties that have already been added into the instance, where they do
// not exist on the newly de-serialized array
for (const [k, v] of Object.entries(instance)) {
if (!Object.prototype.hasOwnProperty.call(arrayInstance, k)) {
arrayInstance[k] = v;
}
}
instance = arrayInstance;
} else if (propertyInstance !== undefined || propertyMapper.defaultValue !== undefined) {
serializedValue = serializer.deserialize(
propertyMapper,
propertyInstance,
propertyObjectName,
options,
);
instance[key] = serializedValue;
}
}
}
const additionalPropertiesMapper = mapper.type.additionalProperties;
if (additionalPropertiesMapper) {
const isAdditionalProperty = (responsePropName: string): boolean => {
for (const clientPropName in modelProps) {
const paths = splitSerializeName(modelProps[clientPropName].serializedName);
if (paths[0] === responsePropName) {
return false;
}
}
return true;
};
for (const responsePropName in responseBody) {
if (isAdditionalProperty(responsePropName)) {
instance[responsePropName] = serializer.deserialize(
additionalPropertiesMapper,
responseBody[responsePropName],
objectName + '["' + responsePropName + '"]',
options,
);
}
}
} else if (responseBody && !options.ignoreUnknownProperties) {
for (const key of Object.keys(responseBody)) {
if (
instance[key] === undefined &&
!handledPropertyNames.includes(key) &&
!isSpecialXmlProperty(key, options)
) {
instance[key] = responseBody[key];
}
}
}
return instance;
}