3

I have the following xml which I'm trying to deserialize as follows but I get error:

There was an error reflecting type 'System.Collections.Generic.List`1[MyApp.Models.Field]

Below is my code

public class FieldList
{
    [XmlArray("fields")]
    [XmlArrayItem("field")]
    List<Field> Fields { get; set; }
}

public class Field
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("required")]
    public bool Required { get; set; }

    [XmlAttribute("label")]
    public string Label { get; set; }

    [XmlAttribute("name")]
    public string Name { get; set; }

    [XmlElement("option")]
    [JsonProperty("values")]
    public List<Option> Options { get; set; }
}

public class Option
{
    [XmlAttribute("label")]
    public string Label { get; set; }

    [XmlAttribute("value")]
    public string Value { get; set; }

    [XmlAttribute("selected")]
    public bool Selected { get; set; }

    /// <remarks/>
    [XmlIgnore()]
    public bool SelectedSpecified { get; set; }

    [XmlText]
    public string Text { get; set; }
}

var xml =  @"<?xml version=""1.0"" ?>
   <form-template>
    <fields>
        <field type=""select"" required=""true"" label=""Cars"" name=""cars"" >
            <option label=""Toyota"" value=""Toyota"" selected=""true"">Toyota</option>
            <option label=""Nisan"" value=""Nisan"" >Nisan</option>
        </field>
    </fields>
   </form-template>";


 var serializer = new XmlSerializer(typeof(FieldList), new XmlRootAttribute("form-template"));
 var stringReader = new StringReader(xml);
 var xmlFields = (FieldList)serializer.Deserialize(stringReader);

What am I doing wrong?

* UPDATE *

As per comments below changing

 public IEnumerable<Option> Options { get; set; }

To

  public List<Option> Options { get; set; }

fixes the error but now nothing is deserialized - the variable xmlFields is empty??? Do I need to read from a particular node or should it not matter?

adam78
  • 9,668
  • 24
  • 96
  • 207

3 Answers3

3

XmlSerializer cannot serialize abstract properties so please change IEnumerable to List like below,

public IEnumerable<Option> Options { get; set; }

to

public List<Option> Options { get; set; }

You root element is not matching with the XML structure you have. I have edited XML like below,

var xml = "<?xml version=\"1.0\" ?>" +
                    "  " +
                    "    <Fields>" +
                    "        <Field Type=\"select\" Required=\"true\" Label=\"Cars\" Name=\"cars\" >" +
                    "            <option label=\"Toyota\" lalue=\"Toyota\" selected=\"true\">Toyota</option>" +
                    "            <option label=\"Nisan\" value=\"Nisan\" >Nisan</option>" +
                    "        </Field>" +
                    "    </Fields>";

and deserializing codes as,

XmlSerializer serializer = new XmlSerializer(typeof(List<Field>), new XmlRootAttribute("Fields"));

    var stringReader = new StringReader(xml);
        List<Field> xmlFields = (List<Field>)serializer.Deserialize(stringReader);

Now it is Deserializing fine. Please see the output,

enter image description here

Vijayanath Viswanathan
  • 8,027
  • 3
  • 25
  • 43
  • Changing to `List – adam78 Oct 17 '17 at 10:29
  • Is the `Option` class marked with the `[Serializable]` attribute? Does it have a parameterless CTOR? – Moerwald Oct 17 '17 at 10:33
  • @Moerwald - The `XmlSerializer` class does not use the `Serializable` attribute. – Alexander Petrov Oct 17 '17 at 11:01
  • @adam78 your class structure not matching with the xml you have. Try removing the root element '' and try. I have updated the answer – Vijayanath Viswanathan Oct 17 '17 at 11:32
  • @VijayanathViswanathan the problem is with `public class Field {}`. In my xml `` is lower case and in my Class it is title case. When I change it to title case in my xml it works. Unfortunately I cant change xml so how can I add an xml attribute to the class name. `[XmlAttribute()]` can only be applied to properties. – adam78 Oct 17 '17 at 12:19
2

Let's look at your code.

new XmlRootAttribute("form-template") maps to <form-template> node.

public class Field maps to <field node.

But nothing maps to <fields> node.

Add following class:

public class FieldList
{
    [XmlArray("fields")]
    [XmlArrayItem("field")]
    public List<Field> Fields { get; set; }
}

It should work:

var serializer = new XmlSerializer(typeof(FieldList), new XmlRootAttribute("form-template"));
var stringReader = new StringReader(xml);
var xmlFields = (FieldList)serializer.Deserialize(stringReader);

Also, you should add a property to the Option class:

[XmlText]
public string Text { get; set; }

Update.

You can get rid of the FieldList class. But then you have to manually skip part of xml nodes.

List<Field> xmlFields;
var serializer = new XmlSerializer(typeof(List<Field>), new XmlRootAttribute("fields"));

// You can read from a stream or from a StringReader instead of a file
using (var xmlReader = XmlReader.Create("test.xml"))
{
    // Skip <form-template> node
    xmlReader.ReadToFollowing("fields");

    xmlFields = (List<Field>)serializer.Deserialize(xmlReader);
}

Add XmlType attribute to your class:

[XmlType("field")]
public class Field
Alexander Petrov
  • 13,457
  • 2
  • 20
  • 49
  • I've made the amendments as per your solution but `xmlFields` is still null? – adam78 Oct 17 '17 at 11:54
  • is there any way to start reading the xml from `` node so that I can get rid of the `FieldList` class without modifying the existing xml? – adam78 Oct 17 '17 at 13:29
1

You need to generate the classes correctly to which you want to serialize the xml to. Copy your correct XML without double "double quotes" into clipboard, go to any cs file in the solution in Visual Studio. Click on Edit->Paste Special-> Paste XML as classes. That will generate classes correctly for conversion in your cs file. Then, change these to auto-implemented properties instead of using private variables to reduce the clutter on screen.

With the set of classes below, deserialize as

XmlSerializer serializer = new XmlSerializer(typeof(FormTemplate));
            var xml = @"<?xml version=""1.0"" ?>
   <form-template>
    <fields>
        <field type=""select"" required=""true"" label=""Cars"" name=""cars"" >
            <option label=""Toyota"" value=""Toyota"" selected=""true"">Toyota</option>
            <option label=""Nisan"" value=""Nisan"" >Nisan</option>
        </field>
    </fields>
   </form-template>";
            var stringReader = new StringReader(xml);
            var xmlFields = serializer.Deserialize(stringReader);

Auto Generated set of classes modified to change properties to auto-implemented properties.

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute("form-template", Namespace = "", IsNullable = false)]
public partial class FormTemplate
{
    /// <remarks/>
    public Fields fields { get; set; }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class Fields
{
    /// <remarks/>
    public SingleField field { get; set; }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class SingleField
{
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("option")]
    public Option[] option { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string type { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public bool required { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string label { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string name { get; set; }
  }

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class Option
{
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string label { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string value { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public bool selected { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool selectedSpecified { get; set; }

    /// <remarks/>
    [System.Xml.Serialization.XmlTextAttribute()]
    public string Value { get; set; }

}
Amit Kumar Singh
  • 4,393
  • 2
  • 9
  • 22