简体   繁体   中英

Deserialize XML generated from .NET Core 3.1 Web API

.NET Core 2.x used to generate "normal" XML in a format that was easily deserializeable using standard methods, but now, in v3.1.5 at least, using AddXmlDataContractSerializerFormatters in your startup configuration to enable an API to return XML returns responses that look like this...

<ArrayOfArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <ArrayOfKeyValueOfstringanyType>
        <KeyValueOfstringanyType>
            <Key>districtId</Key>
            <Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">122</Value>
        </KeyValueOfstringanyType>
        <KeyValueOfstringanyType>
            <Key>amount</Key>
            <Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">1888810.42</Value>
        </KeyValueOfstringanyType>
    </ArrayOfKeyValueOfstringanyType>
    <ArrayOfKeyValueOfstringanyType>
        <KeyValueOfstringanyType>
            <Key>districtId</Key>
            <Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">205</Value>
        </KeyValueOfstringanyType>
        <KeyValueOfstringanyType>
            <Key>amount</Key>
            <Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:decimal">1207736.00</Value>
        </KeyValueOfstringanyType>
    </ArrayOfKeyValueOfstringanyType>
</ArrayOfArrayOfKeyValueOfstringanyType>

When trying to deserialize this in client code using

var serializer = new XmlSerializer(typeof(List<MyDto>));
var reader = new StringReader(content);
var list = (List<MyDto>)serializer.Deserialize(reader);

it understandably throws the exception

InvalidOperationException: <ArrayOfArrayOfKeyValueOfstringanyType xmlns='http://schemas.microsoft.com/2003/10/Serialization/Arrays'> was not expected.

The DTO used in this case is simply

public class MyDto
{
    public decimal DistrictId { get; set; }
    public decimal Amount { get; set; }
}

so it is expecting XML similar to this, which would work fine

<ArrayOfMyDto>
  <MyDto>
    <DistrictId>122</DistrictId>
    <Amount>1888810.42</Amount>
  </MyDto>
  ... more items in list here
</ArrayOfMyDto>

Anyone know how to deserialize this XML format now returned in .NET Core 3.1.5?

UPDATE:

The real issue in my case is that the XML I need to deserialize looks like it does because is a serialized list of ExpandoObjects, not a list of regular DTOs, even though a regular DTO was the source for data in the expando objects.

In the case of a list of regular DTOs, I would have just used AddXmlSeralizerFormatters and it would deserialize lists of DTOs wrapped in "ArrayOf" just fine.

I performed a bunch of tests using AddXmlSerializerFormatters and AddXmlDataContractSerializerFormatters together with lists of regular DTOs and lists of ExpandoObjects to determine the behavior and what could be done without writing custom classes to deserialize this XML repsonse:

From a client perspective:

  1. AddXmlSerializerFormatters CAN handle regular lists of regular objects (DTO) for either serialization or deserialization
  2. AddXmlSerializerFormatters CANNOT handle lists of dynamic objects (ExpandoObject) for either serialization or deserialization (406 Not Acceptable)
  3. AddXmlDataContractSerializerFormatters CAN handle serialization but NOT deserialization of lists of regular objects (DTO) (InvalidOperationException)
  4. AddXmlDataContractSerializerFormatters CAN handle serialization but NOT deserialization of lists of dynamic objects (ExpandoObject) (InvalidOperationException)
  5. Using both Formatters together, with AddXmlSerializerFormatters first in the pipeline, DOES allow serialization and deserialization to be passed through to AddXmlDataContractSerializerFormatters because the former returns 406 Not Acceptable, and the latter's behaviors is observed
  6. Using both Formatters together, with AddXmlDataContractSerializerFormatters first in the pipeline, DOES NOT allow this pass through because InvalidOperationException is returned and halts the processing

From a server perspective:

  1. AddXmlSerializerFormatters CANNOT handle POSTs of even regular objects (DTO) (400 Bad Request) and DOES NOT pass through to AddXmlDataContractSerializerFormatters which CAN handle it

So, in conclusion, the best behavior you can expect without having to write custom DTOs for “ArrayOf” wrapped XML, is to use AddXmlSerializerFormatters followed by AddXmlDataContractSerializerFormatters in your client app, and use ONLY AddXmlDataContractSerializerFormatters in your server app

Or better yet, ditch XML completely on your server and use JSON:P

 private void deserializxml() { string myxmlstr = @"<ArrayOfMyDto> <MyDto> <DistrictId>122</DistrictId> <Amount>1888810.42</Amount> </MyDto> </ArrayOfMyDto>"; XmlSerializer serializer = new XmlSerializer(typeof(List<MyDto>), new XmlRootAttribute("ArrayOfMyDto")); StringReader stringReader = new StringReader(myxmlstr); List<MyDto> addList = (List<MyDto>)serializer.Deserialize(stringReader); }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM