Search code examples
c#xmlserialization

XML Serialization in C# Invalid Operation Exception


I'm trying to serialize the below XML into a class object, which I have listed below. I'm only interested in the planId and name attributes. I want this back as a list<Plan> objects.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<apim:ApimQueryResult xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bns="http://api.platform.boomi.com/" xmlns:apim="http://apim.api.platform.boomi.com/" numberOfResults="3">
    <bns:result xsi:type="apim:Plan" planId="1" name="Client 1" description="Unlimited api access" maxMessageSize="0" rateLimit="0" rateLimitUnit="HOUR" quotaLimit="0" quotaLimitUnit="DAY" status="ENABLED"/>
    <bns:result xsi:type="apim:Plan" planId="2" name="Client 2" description="" maxMessageSize="0" rateLimit="0" rateLimitUnit="HOUR" quotaLimit="0" quotaLimitUnit="DAY" status="ENABLED"/>
    <bns:result xsi:type="apim:Plan" planId="3" name="Client 3" description="" maxMessageSize="0" rateLimit="0" rateLimitUnit="HOUR" quotaLimit="0" quotaLimitUnit="DAY" status="ENABLED"/>
</apim:ApimQueryResult>

Class Plan

public class Plan
{
   public int planId { get; set; }
   public string planName { get; set; }
}
XmlDocument doc = new XmlDocument();
doc.LoadXml(response.Result);

if (doc != null)
{
    var nsmgr = new XmlNamespaceManager(doc.NameTable);
    nsmgr.AddNamespace("bns", "http://api.platform.boomi.com/");
    var nodes = doc.SelectNodes(@"//bns:result", nsmgr);



    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Plan));

    foreach(XmlNode node in nodes)
    {
        XmlNodeReader xmlReader = new XmlNodeReader(node);
        Plan plan = (Plan)xmlSerializer.Deserialize(xmlReader);
        plans.Add(plan);
    }
}

I receive an error:

InvalidOperationException: <result xmlns='http‍://api.platform.boomi.com/'> was not expected.


Solution

  • You are getting the error because the Plan class has planName but the attribute in the xml is actually name. Try adding xml attribute like this:

     public class Plan {    
        public int planId { get; set; }
    
        [XmlAttribute("name")]
        public string planName { get; set; }
    }
    

    Edit to get around new exception:

    I added 2 attributes to Plan. First the XmlTypeAttribute is specifying the other namespace in the response.

    I'm honestly not sure how XmlRootAttribute is helping here but this is the only way I can get it to run 😅. To me, "result" looks like an child element not a root but here we are...

    [XmlType(Namespace = "http://apim.api.platform.boomi.com/")]
      [XmlRoot(ElementName = "result", Namespace = "http://api.platform.boomi.com/")]
      public class Plan
      {
        [XmlAttribute("planId")]
        public int PlanId { get; set; }
    
        [XmlAttribute("name")]
        public string PlanName { get; set; }
      }
    

    Here is how I reproduced it:

    
    using System.Xml;
    using System.Xml.Serialization;
    
    
    public class Program
    {
      [XmlType(Namespace = "http://apim.api.platform.boomi.com/")]
      [XmlRoot(ElementName = "result", Namespace = "http://api.platform.boomi.com/")]
      public class Plan
      {
        [XmlAttribute("planId")]
        public int PlanId { get; set; }
    
        [XmlAttribute("name")]
        public string PlanName { get; set; }
      }
    
      static void Main(string[] args)
      {
        var response = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?>
            <apim:ApimQueryResult xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:bns=""http://api.platform.boomi.com/"" xmlns:apim=""http://apim.api.platform.boomi.com/"" numberOfResults=""3"">
            <bns:result xsi:type=""apim:Plan"" planId=""1"" name=""Client 1"" description=""Unlimited api access"" maxMessageSize=""0"" rateLimit=""0"" rateLimitUnit=""HOUR"" quotaLimit=""0"" quotaLimitUnit=""DAY"" status=""ENABLED""/>
            <bns:result xsi:type=""apim:Plan"" planId=""2"" name=""Client 2"" description="""" maxMessageSize=""0"" rateLimit=""0"" rateLimitUnit=""HOUR"" quotaLimit=""0"" quotaLimitUnit=""DAY"" status=""ENABLED""/>
            <bns:result xsi:type=""apim:Plan"" planId=""3"" name=""Client 3"" description="""" maxMessageSize=""0"" rateLimit=""0"" rateLimitUnit=""HOUR"" quotaLimit=""0"" quotaLimitUnit=""DAY"" status=""ENABLED""/>
            </apim:ApimQueryResult>";
    
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(response);
        var plans = new List<Plan>();
        if (doc != null)
        {
          var nsmgr = new XmlNamespaceManager(doc.NameTable);
          nsmgr.AddNamespace("bns", "http://api.platform.boomi.com/");
          var nodes = doc.SelectNodes(@"//bns:result", nsmgr);
    
          XmlSerializer xmlSerializer = new XmlSerializer(typeof(Plan));
    
          foreach (XmlNode node in nodes)
          {
            XmlNodeReader xmlReader = new XmlNodeReader(node);
            Plan plan = (Plan)xmlSerializer.Deserialize(xmlReader);
            plans.Add(plan);
          }
        }
    
    
        foreach (Plan plan in plans)
        {
          Console.WriteLine(plan.PlanId);
          Console.WriteLine(plan.PlanName);
        }
      }
    }