function deserializeCompositeType()

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;
}