2

I have an input XML string with repeating <row> tags that I'm trying to deserialize into an object, but all the rows except the last one get ignored. Any help would be appreciated.

As an example, after the deserialization, the object I get is:

object.command[0].userTable =
    {OCI.OCITable}
        colHeading: {string[3]}
        colHeadingField: {string[3]}
        row: {string[3]}
        rowField: {string[3]}

This is wrong, because there is only one row in this object, but there should be 4 <row> in the input XML string like this:

  <?xml version="1.0" encoding="ISO-8859-1" ?> 
  <BroadsoftDocument protocol="OCI" xmlns="C" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <sessionId xmlns="">feajiofefeaij</sessionId> 
  <command echo="" xsi:type="UserGetListInServiceProviderResponse" xmlns="">
  <userTable>
      <colHeading>User Id</colHeading> 
      <colHeading>Group Id</colHeading> 
      <colHeading>Name</colHeading>
      <row>
          <col>1</col> 
          <col>A</col> 
          <col>Smith</col> 
      </row>
      <row>
          <col>2</col> 
          <col>A</col> 
          <col>John</col> 
      </row>
      <row>
          <col>3</col> 
          <col>B</col> 
          <col>James</col> 
      </row>
      <row>
          <col>4</col> 
          <col>B</col> 
          <col>Lisa</col> 
      </row>
  </userTable>
  </command>
  </BroadsoftDocument>

The way I'm doing deserialization is:

MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(responseString));
XmlSerializer ser = new XmlSerializer(typeof(OCIMessage));
OCIMessage response = (OCIMessage)ser.Deserialize(memStream);

And the C# class automatically generated by xsd.exe for the OCITable class is this:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "C")]
public partial class OCITable
{

    private string[] colHeadingField;
    private string[] rowField;

    [System.Xml.Serialization.XmlElementAttribute("colHeading", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string[] colHeading
    {
        get
        {
            return this.colHeadingField;
        }
        set
        {
            this.colHeadingField = value;
        }
    }

    [System.Xml.Serialization.XmlArrayAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlArrayItemAttribute("col", typeof(string), Form = System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable = false)]
    public string[] row
    {
        get
        {
            return this.rowField;
        }
        set
        {
            this.rowField = value;
        }
    }
}
3
  • 2
    For row and col change from XmlArrayItemAttribute (all places) to XmlElement. Commented Aug 29, 2016 at 7:32
  • @jdweng could you explain what the difference is? Commented Aug 29, 2016 at 21:07
  • XmlArray creates a 2nd tag like <rows><row> while XElement only creates <row> Commented Aug 29, 2016 at 21:35

1 Answer 1

2

The main problem is that, in your XML, the <row> and <col> elements are a 2d jagged array: there is an outer, repeating set of <row> elements containing an inner, repeating set of <col> elements, each of which has a string value. But in your OCITable class you have modeled this as a 1d array of strings:

public string[] row { get; set; }

This only allows for one outer <row> element. If multiple <row> elements are encountered while parsing, XmlSerializer will overwrite the earlier values with the later - which is exactly what you are seeing.

In order to capture the nested hierarchy of row and column elements, you would instead need to do something like:

public string[][] row { get; set; }

However, if you do, you will encounter the problem described in C# xml serialization remove jagged array element name, namely that XmlSerializer will always serialize a 2d jagged array with an extra outer container element, like so:

<row>
    <col>
        <string>a</string>
    </col>
    <col>
        <string>b</string>
    </col>
</row>

Thus this also will not work.

Instead, you will need to make your row member a simple array of intermediate classes which contain a 1d array of strings, like so:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "C")]
public partial class OCITable
{
    private string[] colHeadingField;

    [System.Xml.Serialization.XmlElementAttribute("colHeading", Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string[] colHeading
    {
        get
        {
            return this.colHeadingField;
        }
        set
        {
            this.colHeadingField = value;
        }
    }

    [System.Xml.Serialization.XmlElement(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public OCITableRow [] row { get; set; }
}

public class OCITableRow
{
    [System.Xml.Serialization.XmlElementAttribute("col", Form = System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable = false)]
    public string[] col { get; set; }
}

Both the row and col arrays are marked with [XmlElement] to indicate they should be serialized as a repeating sequence of elements without an outer container element.

With the classes defined above you will be able to deserialize your <userTable> element. Sample fiddle.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm curious, is it normal to need to tweak the C# classes after generating them from XSD Schemas? I generated those classes with xsd.exe that came with Visual Studio using the schemas provided by the webservice. Do you think the cause of this issue is incomplete Schemas, or I didn't configure the class generator properly, or if it is common to need to tweak the classes by hand after generation? (To be fair, the generated code did have string[][] but I changed it to 1D array because 2D array was crashing my app)
@Synia - usually it's not necessary. Maybe xsd.exe has a problem with jagged arrays without container elements similar to the one mentioned here?
Synia - have to love BroadSoft/BroadWorks and Java! We just ran into the same issue and were headed down this same route based on other articles, @dbc thanks for the working example!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.