0

I have the following XML that I am trying to parse

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:IntegrationActionResponse xmlns:ns2="example.com">
    <ActionResults>
        <SuccessCount>1</SuccessCount>
        <FailureCount>0</FailureCount>
        <ActionResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns2:ParameterizedIntegrationActionResult">
            <Status>OK</Status>
            <resultParams>
                <entry>
                    <key>abc</key>
                    <value>abc</value>
                </entry>
                <entry>
                    <key>abc</key>
                    <value>abc</value>
                </entry>
                <entry>
                    <key>abc</key>
                    <value>abc</value>
                </entry>  
            </resultParams>
        </ActionResult>
    </ActionResults>
</ns2:IntegrationActionResponse>

I'm trying to deserialize it using this model class

using System.Collections.Generic;
using System.Xml.Serialization;
{
    [XmlRoot(
        IsNullable = false,
        ElementName = "IntegrationActionResponse",
        Namespace = "example.com"
    )]
    public class LoadIActionResponseModel
    {
        [XmlElement("ActionResults", Namespace = "")]
        public LoadIActionResults ActionResults { get; set; }
    }

    public class LoadIActionResults
    {
        [XmlElement(ElementName = "SuccessCount")]
        public int SuccessCount { get; set; }

        [XmlElement(ElementName = "FailureCount")]
        public int FailureCount { get; set; }

        [XmlType(TypeName = "ParameterizedIntegrationActionResult", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
        public LoadIActionResult ActionResult { get; set; }
    }
    
    public class LoadIActionResult 
    {
        [XmlElement(ElementName = "Status")]
        public string Status { get; set; }

        [XmlElement(ElementName = "resultParams")]
        public LoadIActionResultParams resultParams {get; set;}
    }
   
    public class LoadIActionEntry
    {
        [XmlElement(ElementName = "key")]
        public string Key { get; set; }

        [XmlElement(ElementName = "value")]
        public string Value { get; set; }
    }
  
    public class LoadIActionResultParams
    {
        [XmlElement(ElementName = "entry")]
        public List<LoadIActionEntry> Entries { get; set; }
    }
}

The root node is being deserialized ok. I have tried using the XmlType annotation to parse out the attributes, but the ActionResult returns null. How do I build my model class to accept the xlmns and xsi attributes on the ActionResult node?

Ken White
  • 123,280
  • 14
  • 225
  • 444
  • Assuming you are using `XmlSerializer` and you generated your classes from the XML sample rather than from some XSD, your problem is the same as the problem from [xsi:type attribute messing up C# XML deserialization](https://stackoverflow.com/a/36365689/3744182). The `xsi:type="ns2:ParameterizedIntegrationActionResult"` attribute indicates that `` corresponds to some polymorphic type hierarchy, but none of the XML-to-c# tools will create it for you automatically, you have to do it manually. Do you need help with that? – dbc Sep 19 '22 at 21:34
  • Also, you can't add `[XmlType(TypeName = "ParameterizedIntegrationActionResult", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]` to `public LoadIActionResult ActionResult { get; set; }`, you will get a compilation error *` Attribute 'XmlType' is not valid on this declaration type. It is only valid on 'class, struct, enum, interface' declarations.*` See https://dotnetfiddle.net/h3mNRZ. – dbc Sep 19 '22 at 22:50

1 Answers1

0

Your basic problem is the same as the problem from Deserialize XML with multiple types and xsi:type attribute messing up C# XML deserialization. The presence of the xsi:type type attribute in the <ActionResult> element:

<ActionResult xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:type=""ns2:ParameterizedIntegrationActionResult"">
   <!--Contents omitted-->
</ActionResult>

indicates that this element must be bound to a polymorphic type hierarchy, with a base type corresponding to ActionResult and subtype corresponding to ParameterizedIntegrationActionResult. But it seems as though you created your c# classes from your XML using some sort of automatic code generation tool such as the ones from Convert XML String to Object -- and unfortunately none of these tools is capable of automatically generating a polymorphic type hierarchy from an XML sample.

Thus it will be necessary to manually fix your data model as follows:

[XmlRoot(IsNullable = false,ElementName = "IntegrationActionResponse",Namespace = "example.com")]
public class LoadIActionResponseModel
{
    [XmlElement("ActionResults", Namespace = "")]
    public LoadIActionResults ActionResults { get; set; }
}

public class LoadIActionResults
{
    [XmlElement(ElementName = "SuccessCount", Namespace = "")]
    public int SuccessCount { get; set; }

    [XmlElement(ElementName = "FailureCount", Namespace = "")]
    public int FailureCount { get; set; }

    [XmlElement(ElementName = "ActionResult", Namespace = "")] // Fixed
    public ActionResultBase ActionResult { get; set; }
}

// Abstract base class added
[XmlType(TypeName="ActionResult", Namespace = "example.com")] // Fixed
[XmlInclude(typeof(ParameterizedIntegrationActionResult))] // Added
public abstract class ActionResultBase // Added
{
}

// LoadIActionResult renamed and made a subclass
[XmlType(TypeName="ParameterizedIntegrationActionResult", Namespace = "example.com")] // Fixed
public class ParameterizedIntegrationActionResult : ActionResultBase
{
    [XmlElement(ElementName = "Status", Namespace = "")]
    public string Status { get; set; }

    [XmlElement(ElementName = "resultParams", Namespace = "")]
    public LoadIActionResultParams resultParams {get; set;}
}

public class LoadIActionEntry
{
    [XmlElement(ElementName = "key")]
    public string Key { get; set; }

    [XmlElement(ElementName = "value")]
    public string Value { get; set; }
}

public class LoadIActionResultParams
{
    [XmlElement(ElementName = "entry")]
    public List<LoadIActionEntry> Entries { get; set; }
}

Notes:

  • You must add [XmlInclude(typeof(TDerivedActionBase))] to ActionResultBase for every subclass of ActionResultBase you wish to serialize.

  • From the XML shown there is no way to tell which properties should belong to ActionResultBase and which should belong to ParameterizedIntegrationActionResult so I assigned them all to the derived type.

  • Your code has an additional error: you attempt to apply XmlTypeAttribute to the ActionResult:

    [XmlType(TypeName = "ParameterizedIntegrationActionResult", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    public LoadIActionResult ActionResult { get; set; }
    

    However this attribute can only be applied to a class, struct, enum, or interface. You should instead use XmlElementAttribute.

  • While no .NET code generation tool with which I am familiar will generate a polymorphic type hierarchy from an XML sample, some such as xsd.exe will generate one correctly from an XSD schema. If you have a schema, you should generate your classes from it rather than from the XML.

Demo fiddle here.

dbc
  • 104,963
  • 20
  • 228
  • 340