1

I have to serialize my objects to a XML document in a certain order.

<?xml version="1.0"?>
<__Root __version="1.1" __encryption="2">
  <__service __serviceType="Test">
  <__inputData>
   <BaseToChange>
    <__name>TestName</__name>
   </BaseToChange>
  </__inputData>
  <__perform> 
   <__eventName>Event</__eventName>
  </__perform>    
  <__inputData>
      <ObjectChanges>
        <Name>Test</Name>
      </ObjectChanges>
    </__inputData>
    <__execute />
    <__requestData>
      <CompletionMsg />
    </__requestData>
  </__service>
</__Root>

The problem I have now is that I'm not able to serialize my List<InputData> with the Element Perform in between.

public class Service
{
    [XmlAttribute("__serviceType")]
    public string ServiceType { get; set; }

    [XmlElement("__perform")]
    public Perform Perform { get; set; }

    [XmlElement("__inputData")]
    public List<InputData> InputData{ get; set; }

    [XmlElement("__execute")]
    public Execute Execute { get; set; }

    [XmlElement("__requestData")]
    public RequestData RequestData{ get; set; }

    public Service() { }
}

The order has to be as shown. So first <__inputData>, then <__perform>, followed by any remaining <__inputData>.

I already tried to separate the Properties and therefore the XmlElements but as soon I want to serialize with two elements having the same name I get an error.

Does anyone have an idea how to accomplish that?

dbc
  • 104,963
  • 20
  • 228
  • 340
Shorty
  • 43
  • 3
  • Possible Duplicate: http://stackoverflow.com/questions/1018490/net-serialization-ordering – Dave Becker Mar 10 '17 at 10:59
  • What do you mean in between? do you mean item 1 of the list and then perform and then item 2 in the list? it sounds like your structure should be redesigned – Ofir Winegarten Mar 10 '17 at 11:53
  • @Ofir Winegarden: Exactly. I need this structure otherwise the system I try to create an object in (via XML) throws an error. – Shorty Mar 10 '17 at 13:17
  • 1) How do you want to control where the `<__perform>` element appears? It is always after the first `<__inputData>` or can it vary? 2) What does your `InputData` type look like? It seems it sometimes has a `` element, and sometimes an `` element? – dbc Mar 10 '17 at 15:20
  • The order has to be as shown. So first `<__inputData>`, then <__perform>`` followed by `<__inputData>`. The type of 'InputData' has indeed the two objects: `BaseToChange` and `ObjectChanges` – Shorty Mar 10 '17 at 15:28

1 Answers1

0

You can take advantage of the polymorphic element functionality [XmlElement(Type = typeof(TElement))] to create a surrogate object array that contains both the Perform and InputData objects, in the correct sequence:

public class Service
{
    [XmlAttribute("__serviceType")]
    public string ServiceType { get; set; }

    [XmlIgnore]
    public Perform Perform { get; set; }

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

    [XmlElement("__perform", Type = typeof(Perform))]
    [XmlElement("__inputData", Type = typeof(InputData))]
    public object[] XmlInputAndPerformance
    {
        get
        {
            var inputData = (InputData ?? Enumerable.Empty<InputData>()).Cast<object>();
            var performData = Perform == null ? Enumerable.Empty<object>() : new object[] { Perform };

            return inputData.Take(1)
                .Concat(performData)
                .Concat(inputData.Skip(1))
                .ToArray();
        }
        set
        {
            if (value == null)
                return;
            var newInputs = value.OfType<InputData>().ToList();
            var newPerform = value.OfType<Perform>().ToList();
            if (newInputs.Count + newPerform.Count != value.Length)
                throw new ArgumentException("Unknown type.");
            if (newPerform.Count > 1)
                throw new ArgumentException("Too many Perform objects.");

            if (newPerform.Count > 0)
                Perform = newPerform[0];
            (InputData = InputData ?? new List<InputData>()).AddRange(newInputs);
        }
    }

    [XmlElement("__execute")]
    public Execute Execute { get; set; }

    [XmlElement("__requestData")]
    public RequestData RequestData { get; set; }

    public Service() { }
}

Note that the original InputData and Perform properties have been marked with [XmlIgnore] and that two [XmlElement(name, Type = typeof(TElement))] attributes have been added, one for InputData and one for Perform.

Sample fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340