Home > OS >  Deserialize XML into object with dynamic child elements
Deserialize XML into object with dynamic child elements

Time:02-18

I'm trying to deserialize some xml into a C# object. The trick is for the most part, I know what this object will look like. However, this is one child that has dynamic elements.

(here is an example)

<measurement>
  <Time>2021-02-02</Time>
  <ID>1</ID>
  <LeftWheel>
  <ValuesRead>
    <DynamicValue>12.3</DynamicValue>
    <DynamicValue2>2.3</DynamicValue2>
    <DynamicValue4>1.3</DynamicValue4>
    <DynamicValue3>10.3</DynamicValue3>
  </ValuesRead>
  </LeftWheel>
      <RightWheel>
  <ValuesRead>
    <DynamicValue>12.3</DynamicValue>
    <DynamicValue2>2.3</DynamicValue2>
    <DynamicValue6>1.3</DynamicValue6>
    <DynamicValue10>10.3</DynamicValue10>
  </ValuesRead>
  </RightWheel>
</measurement>

In this XML, Measurement, Time, and ID are always going to in the object. The LeftWheel and RightWheel elements are always going to be there with ValuesRead, but the ValuesRead children are dynamic and can be anything.

I have tried making a C# object to reflect most the structure, and then using the XmlSerializer.UnknownElement to pick up the unknown elements in the ValuesRead element, but I cannot link it to the parent above to know if it is on the LeftWheel or RightWheel.

XmlSerializer serializer = new XmlSerializer(typeof(FVISSiteEvent));

serializer.UnknownElement  = UnknownElementFound;

Is there a way I can define the LeftWheel and RightWheel classes to be dynamic for the serialization, while having the other classes not dynamic?

CodePudding user response:

You should be able to use the UnknownElementFound event to manually handle these aspects of serialization. See: Serialize XML array of unknown element name

Other options could be to specify the types you expect to see as XmlElementAtrribute decorated properties and they will just be null if they aren’t deserialized.

There’s also the nuclear option of implementing IXmlSerializable in your class and taking full control of the deserialization.

CodePudding user response:

Uses Custom Serializer :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Linq;
using System.Xml.Schema;

namespace ConsoleApplication16
{
    class Program
    {
        const string INPUT_FILENAME = @"c:\temp\test.xml";
        const string OUTPUT_FILENAME = @"c:\temp\test1.xml";
        static void Main(string[] args)
        {
            XmlReader reader = XmlReader.Create(INPUT_FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Measurement));
            Measurement measurement = (Measurement)serializer.Deserialize(reader);

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            XmlWriter writer = XmlWriter.Create(OUTPUT_FILENAME,settings);
            serializer.Serialize(writer, measurement);
 
        }
    }
    [XmlRoot("measurement")]
    public class Measurement
    {
        public DateTime Time { get; set; }
        public int ID { get; set; }

        [XmlArray("LeftWheel")]
        [XmlArrayItem("ValuesRead")]
        public List<Wheel> leftWheel { get; set; }

        [XmlArray("RightWheel")]
        [XmlArrayItem("ValuesRead")]
        public List<Wheel> rightWheel { get; set; }
    }
    public class Wheel : IXmlSerializable
    {
        List<decimal> values { get; set; }

        // Xml Serialization Infrastructure

        public void WriteXml(XmlWriter writer)
        {
            int count = 0;
            XElement valuesRead = new XElement("ValuesRead");
            for (int i = 0; i < values.Count; i   )
            {
                valuesRead.Add(new XElement("ValuesRead"   (i == 0? "" : i.ToString()), values[i]));
            }
            writer.WriteRaw(valuesRead.ToString());

        }

        public void ReadXml(XmlReader reader)
        {
            XElement values = (XElement)XElement.ReadFrom(reader);
            this.values = values.Elements().Where(x => x.Name.LocalName.StartsWith("DynamicValue")).Select(x => (decimal)x).ToList(); 
        }

        public XmlSchema GetSchema()
        {
            return (null);
        }


        
    } 
}
  • Related