How to force a XmlSerializer to serialize elements

2020-04-18 04:28发布

问题:

I've given some predefined XML similar to this:

<?xml version="1.0" encoding="utf-8"?>
<Root xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Points>
      <Point X="1.345" Y="7.45" />
      <Point X="1.123" Y="5.564" />
      <Point X="3.34" Y="2.5345" />
   </Points>
   <!-- and a bunch of other attributes and structures which are perfectly serialized and deserialized by the XmlSerializer -->
</Root>

And my goal is to deserialize it into a List<System.Windows.Point> using a XmlSerializer instance and vice versa. Therefore I've defined type like the following:

[Serializable]
[XmlRoot("Root")]
public class RootClass
{
   public List<System.Windows.Point> Points { get; set; }
   /* and more properties */
}

My problem is that the XmlSerializer interprets the framework properties as XmlElement. In order to that they are only read and written as such, but not as attributes as required.

One solution I thought about, was defining a custom point type, which defines the XmlAttribtueAttribute for each coordinate property. And this custom point gets mapped to the System.Windows.Point structure. This looked like the following:

[XmlIgnore]
public List<Point> Points { get; set; }

[XmlArray("Points")]
[XmlArrayItem("Point")]
public List<CustomSerializedPoint> CustomSerializedPoints
{
    get { return this.Points.ToCustomSerializedPointList(); }
    set { this.Points = value.ToPointList(); }
}

But for this solution i've noticed, that the setter is never called, and the XmlSerializer calls the getter of CustomSerializedPoints about five times. It expects that there is a backing list, which has the same reference each call and which is never null. In order to that requirements, this is no solution for me, because I would need to keep the List<CustomSerializedPoints> in memory just for writing the points using attributes instead of elements.

So has someone a more practiable solution?

Additionally my XmlSerializer code:

/* ... */
var serializer = new XmlSerializer(typeof(RootClass));
TextReader textReader = new StreamReader("file.xml");
(RootClass)serializer.Deserialize(textReader);
/* ... */

回答1:

You can change the way a class is being serialized/deserialized by altering its serialization attributes at runtime. XmlAttributeOverrides class provides such possibility. The following example code correctly deseralizes the XML you've provided:

XmlAttributes xa = new XmlAttributes();
XmlAttributes ya = new XmlAttributes();

xa.XmlAttribute = new XmlAttributeAttribute("X");
ya.XmlAttribute = new XmlAttributeAttribute("Y");

XmlAttributeOverrides xao = new XmlAttributeOverrides();
xao.Add(typeof(System.Windows.Point), "X", xa);
xao.Add(typeof(System.Windows.Point), "Y", ya);

var serializer = new XmlSerializer(typeof(RootClass), xao);
TextReader textReader = new StreamReader("file.xml");

var result = (RootClass)serializer.Deserialize(textReader);