9

How do I serialize a list without the outer element using the Data Contract Serializer? I am using .Net 3.5. I have a class that contains a list, amongst other things, that I wish to serialize without the outer element to be compliant with the pertinent XSD:

[DataContract(Name="MyClass")]
public class MyClass
{
...
[DataMember(Name="Parameters")]
public List<Parameter> Parameters;
...
}

[DataContract(Name="Parameter")]
public struct Parameter
{
    [DataMember(Name="ValueName")]string ValueName;
    [DataMember(Name="Value")]int Value;
    public Parameter(string ValueName, int Value)
    {
        this.ValueName = ValueName;
        this.Value = Value;            
    }
}

The above serializes as (assuming only one Parameter in the list):

<MyClass>
    <Parameters>
       <Parameter>
           <ValueName></ValueName>
           <Value></Value>
       </Parameter>
    </Parameters>
</MyClass>

I would like to serialize it as follows:

<MyClass> 
       <Parameter>
           <ValueName></ValueName>
           <Value></Value>
       </Parameter>
</MyClass>

Using the XmlSerializer I can do this by applying the [XmlElement] to the list:

[XmlElement ("Parameter")]
public List<Parameter> Parameters;

However I do not want to use the XmlSerializer because my class has a few properties that are not serialization friendly and I was hoping to deal with those using the [OnSerializing] family of attributes.

Thanks.

chrisj
  • 93
  • 1
  • 3
  • You don't have much control over message formating with DataContracts. You might need to use MessageContract - see http://msdn.microsoft.com/en-us/library/ms730255.aspx – StuartLC Dec 21 '11 at 14:09

3 Answers3

5

The DataContract serializer does not allow this degree of control over the resulted XML, you will have to use instead the XmlSerializer in order to achieve this.

Migol
  • 8,161
  • 8
  • 47
  • 69
Pop Catalin
  • 61,751
  • 23
  • 87
  • 115
  • OK thanks. Whenever I search for something and can't find it, it usually is because it cannot be done or it is so stupid no one else thought about doing it. Thanks for confirming the former ;-) – chrisj Dec 21 '11 at 17:54
  • 1
    How can I use the XmlSerializer to control the result? – natenho Sep 18 '14 at 22:05
  • @natenho see the question, the OP has already a solution for that at the bottom of the question. – Pop Catalin Sep 19 '14 at 11:33
  • DataContractSerializer can produce this output as shown below. It is wrong to say it cannot – Simon Dowdeswell May 25 '18 at 03:12
1

The below works using MessageContracts although is a 'hack' - it attributes the "MyClass" element to the List member and excludes the wrapper namespace for "MyClass".

[ServiceContract(Namespace="")]
public interface IService1
{
    [OperationContract]
    MyClass GetParameters();
    // TODO: Add your service operations here
}

[DataContract(Namespace="")]
public class Parameter
{
    [DataMember]
    public string ValueName
    {
        get;
        set;
    }
    [DataMember]
    public int Value
    {
        get;
        set;
    }

    public Parameter(string ValueName, int Value) 
    { 
        this.ValueName = ValueName; 
        this.Value = Value; 
    } 
}

[MessageContract(IsWrapped = false, WrapperNamespace="")]
public class MyClass
{
    [MessageBodyMember(Name = "MyClass", Namespace = "")]
    public List<Parameter> Parameters
    {
        get;
        set;
    }
}
StuartLC
  • 104,537
  • 17
  • 209
  • 285
0

Use a collection data contract:

    [CollectionDataContract(Name = "MyClass", ItemName = "Parameter")]
    public class ParameterList : List<Parameter>
    {

    }

Here is the actual code:

public class TestSerialize
{
    [DataContract(Name = "Parameter")]
    public struct Parameter
    {
        [DataMember(Name = "ValueName")] string ValueName;
        [DataMember(Name = "Value")] int Value;
        public Parameter(string ValueName, int Value)
        {
            this.ValueName = ValueName;
            this.Value = Value;
        }
    }

    [CollectionDataContract(Name = "MyClass", ItemName = "Parameter")]
    public class ParameterList : List<Parameter>
    {

    }


    public string Serialize(ParameterList plist)
    {
        var serializer = new DataContractSerializer(plist.GetType());
        var output = new StringBuilder();
        var xmlWriter = XmlWriter.Create(output);

        serializer.WriteObject(xmlWriter, plist);
        xmlWriter.Close();

        return output.ToString();
    }


    public void Serialize_produces_2Levels_of_xml()
    {
        ParameterList p = new ParameterList
        {
            new Parameter("First", 1),
            new Parameter("Second", 2),
        };

        var xml = Serialize(p);
    }
}

if you run this you will get the following XML:

<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Serialize.Test">
    <Parameter>
        <Value>1</Value>
        <ValueName>First</ValueName>
    </Parameter>
    <Parameter>
        <Value>2</Value>
        <ValueName>Second</ValueName>
    </Parameter>
</MyClass>
Simon Dowdeswell
  • 1,001
  • 11
  • 19
  • I dont know why someone downvoted this (perhaps they couldleave a comment as to the reason. It worked for me and is documented here: https://msdn.microsoft.com/en-us/library/system.runtime.serialization.collectiondatacontractattribute(v=vs.90).aspx – Simon Dowdeswell Sep 26 '15 at 01:37
  • 4
    I suppose it was downvoted because it will still result in a outer element with repeating child elements. The question specifically asks for a way to get rid of the outer element. – Gruff Apr 12 '16 at 19:09
  • The question did not ask to get rid of the outer element (MyClass) it asks to get rid of the one underneath(Parameters) ... which is exactly what this attribute does! – Simon Dowdeswell Jun 04 '16 at 00:25
  • With outer element I was talking about the element (or in the question example), while is the inner element. – Gruff Jun 10 '16 at 05:31
  • @Gruff is correct, it does not work. Maybe if the example was fleshed out we'd see what we did incorrectly. – Marc Bernier Mar 21 '18 at 18:48
  • @MarcBernier I have pasted in the code, as requested. It produces the exact output required (pasted in also). Can you now undo your down vote and amend your comment incorrectly stating it doesn't work. – Simon Dowdeswell May 25 '18 at 00:56
  • @Gruff as you can see the output DOES NOT produce any element and DOES repeat the element as required from the original question. – Simon Dowdeswell May 25 '18 at 03:09
  • I do not see why the answer currently marked as correct is the right one - as far as I can see this is the best answer for the question as it produces the required output using the DataContractSerializer as originally requested – Simon Dowdeswell May 25 '18 at 03:10
  • 1
    @SimonDowdeswell please note, in your code you are simply serializing an instance of `ParameterList`. The question has a `MyClass` class with a `Parameters` property. If `MyClass` has other properties too (which is what OP is explicitly stating: "I have a class that contains a list, amongst other things"), you solution will not work. – Gruff May 28 '18 at 09:09
  • @Gruff the errant output given by the questioner does not contain any other elements and also no elements are requested in the desired output – Simon Dowdeswell May 29 '18 at 00:12
  • @Gruff - the question IN BOLD is "How do I serialize a list without the outer element" the attempted structure does not work. By moving the list to the parent class and attaching the attribute as i gave in the original answer you get what is requested! If you don't want to make that change that is your decision ... but that does not make the answer wrong, which is what you are saying ... and down voting. – Simon Dowdeswell May 29 '18 at 01:47
  • @SimonDowdeswell - I actually did not down vote your answer, I simply pointed out why someone else would, as a courtesy, because you stated that you do not know why someone would. – Gruff May 29 '18 at 14:18