Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XML serialization results in duplicate nodes

I have an object structure that I'm trying to serialize to xml that's resulting in a duplicated node level. I'm pretty sure it has something to do with subclassing because I had to implement my own deserialization but I'm not sure exactly what's going on in the other direction. The same xml structure is used as input when deserializing my data at application launch as when reserializing it to be saved later.

Here's what the faulty output xml looks like:

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Exercises>
    <Exercise>
      <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
        <Title>Test Title</Title>
        <Instructions>Downloaded Update Instructions</Instructions>
      </Exercise>
    </Exercise>
  </Exercises>
</Keyboarding>

Here's what it should look like (and looks like on initial deserialization):

<Keyboarding xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Exercises>
      <Exercise Id="3" ActivityNumber="5" SubActivityNumber="b" Type="Standard">
        <Title>Test Title</Title>
        <Instructions>Downloaded Update Instructions</Instructions>
      </Exercise>
  </Exercises>
</Keyboarding>

The root object contains a collection of exercises. Exercise is my base class, while subclasses are determined by the Type attribute. I'm using a custom xml serializer to build the objects of varying derived types from Exercise because it allows me to use reflection to match any of the 2 dozen or so potential derived types, which are in an unknown order and quantity when first read in by my application.

Here's the root element's collection, using the custom xml serializer:

[XmlArray("Exercises")]
[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]
public Collection<Exercise> UnprocessedExercises { get; set; }

My base exercise class is declared as follows:

[Serializable]
[XmlType(AnonymousType = true)]
public class Exercise

And my derived class is declared as follows:

[Serializable]
[XmlType(AnonymousType = true)]
[XmlRoot(ElementName = "Exercise", IsNullable = false)]
public class StandardExercise : Exercise

Here's the writer portion of my custom xml serializer:

public class ExerciseXmlSerializer<T> : IXmlSerializable where T : class
{
    private T _data;
    ...
    public void WriteXml(XmlWriter writer)
    {
        Type type = _data.GetType();

        new XmlSerializer(type).Serialize(writer, _data);
    }
    ...
}

In the WriteXml() method, the type variable is properly set to the derived type, so why does it create a node level for both the base type and the derived type? How do I prevent it from doing so?

like image 817
HotN Avatar asked Nov 14 '22 12:11

HotN


1 Answers

I think your problem is with this line:

[XmlArrayItem("Exercise", Type = typeof (ExerciseXmlSerializer<Exercise>))]

I don't think that you want to indicate that the type of the items are a type of the serializer. If you want to implement IXmlSerializable, it needs to be on the class to be serialized.

Here is what I got to work:

public class Keyboarding
{
    [XmlArray("Exercises")]
    [XmlArrayItem("Exercise")]
    public Collection<Exercise> UnprocessedExercises { get; set; }

    public Keyboarding()
    {
        UnprocessedExercises = new Collection<Exercise>();
        UnprocessedExercises.Add(new StandardExercise());
    }
}

[XmlInclude(typeof(StandardExercise))]
public class Exercise
{
}
[Serializable]
public class StandardExercise : Exercise
{
}

This produces output similar to the following:

<?xml version=\"1.0\"?>
<Keyboarding xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
  <Exercises>
    <Exercise xsi:type=\"StandardExercise\" />
  </Exercises>
</Keyboarding>

If you don't want the xsi:type included in the output, you can use the answer to this question.

like image 58
competent_tech Avatar answered Nov 16 '22 04:11

competent_tech