Сериализатор контракта данных - как опустить внешний элемент коллекции

Как сериализовать список без внешнего элемента с помощью сериализатора контракта данных? Я использую .Net 3.5. У меня есть класс, содержащий список, среди прочего, который я хочу сериализовать без внешнего элемента, чтобы он соответствовал соответствующему 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;            
    }
}

Вышеупомянутое сериализуется как (при условии, что в списке только один параметр):

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

Я хотел бы сериализовать его следующим образом:

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

Используя XmlSerializer, я могу сделать это, применив [XmlElement] к списку:

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

Однако я не хочу использовать XmlSerializer, потому что у моего класса есть несколько свойств, не поддерживающих сериализацию, и я надеялся иметь дело с теми, которые используют семейство атрибутов [OnSerializing].

Спасибо.


person chrisj    schedule 21.12.2011    source источник
comment
У вас нет особого контроля над форматированием сообщений с помощью DataContracts. Возможно, вам потребуется использовать MessageContract - см. msdn.microsoft.com/en-us/ библиотека / ms730255.aspx   -  person StuartLC    schedule 21.12.2011


Ответы (3)


Сериализатор DataContract не позволяет такой степени контроля над результирующим XML, вам придется вместо этого использовать XmlSerializer, чтобы добиться этого.

person Pop Catalin    schedule 21.12.2011
comment
Хорошо спасибо. Когда я что-то ищу и не могу найти, обычно это происходит потому, что это невозможно сделать или это настолько глупо, что никто другой не подумал об этом. Спасибо за подтверждение первого ;-) - person chrisj; 21.12.2011
comment
Как я могу использовать XmlSerializer для контроля результата? - person natenho; 19.09.2014
comment
@natenho видите вопрос, у OP уже есть решение для этого внизу вопроса. - person Pop Catalin; 19.09.2014
comment
DataContractSerializer может производить этот вывод, как показано ниже. Неправильно говорить, что не может - person Simon Dowdeswell; 25.05.2018

Используйте контракт на сбор данных:

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

    }

Вот фактический код:

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);
    }
}

если вы запустите это, вы получите следующий 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>
person Simon Dowdeswell    schedule 24.07.2014
comment
Я не знаю, почему кто-то проголосовал против этого (возможно, они могли оставить комментарий о причине. У меня это сработало и описано здесь: msdn.microsoft.com/en-us/library/ - person Simon Dowdeswell; 26.09.2015
comment
Я полагаю, что за него проголосовали против, потому что это все равно приведет к появлению внешнего элемента ‹ParameterList› с повторяющимися дочерними элементами ‹Parameter›. В вопросе конкретно говорится о способе избавления от внешнего элемента. - person Gruff; 12.04.2016
comment
Вопрос не касался избавления от внешнего элемента (MyClass), он просит избавиться от того, что находится под ним (Параметры) ... что именно то, что делает этот атрибут! - person Simon Dowdeswell; 04.06.2016
comment
С внешним элементом я говорил об элементе ‹ParameterList› (или ‹Parameters› в примере вопроса), а ‹Parameter› - это внутренний элемент. - person Gruff; 10.06.2016
comment
@Gruff прав, не работает. Возможно, если бы пример был конкретизирован, мы бы увидели, что делаем неправильно. - person Marc Bernier; 21.03.2018
comment
@MarcBernier Я вставил код, как и просили. Он производит точный требуемый результат (также вставлен). Можете ли вы теперь отменить свой голос против и исправить свой комментарий, в котором указано, что он не работает. - person Simon Dowdeswell; 25.05.2018
comment
@Gruff, как вы можете видеть, вывод НЕ создает никаких элементов ‹ParameterList› и ДЕЙСТВИТЕЛЬНО повторяет элемент ‹Parameter›, как требуется из исходного вопроса. - person Simon Dowdeswell; 25.05.2018
comment
Я не понимаю, почему ответ, который в настоящее время помечен как правильный, является правильным - насколько я вижу, это лучший ответ на вопрос, поскольку он дает требуемый результат с использованием DataContractSerializer, как первоначально запрашивалось. - person Simon Dowdeswell; 25.05.2018
comment
@SimonDowdeswell, обратите внимание, в вашем коде вы просто сериализуете экземпляр ParameterList. В вопросе есть MyClass класс со свойством Parameters. Если MyClass также имеет другие свойства (это то, что OP явно заявляет: у меня есть класс, который, помимо прочего, содержит список), ваше решение не будет работать. - person Gruff; 28.05.2018
comment
@Gruff ошибочный вывод, предоставленный вопрошающим, не содержит никаких других элементов, а также никакие элементы не запрашиваются в желаемом выводе - person Simon Dowdeswell; 29.05.2018
comment
@Gruff - вопрос, выделенный жирным шрифтом: как сериализовать список без внешнего элемента, попытка создания структуры не работает. Переместив список в родительский класс и прикрепив атрибут, который я дал в исходном ответе, вы получите то, что запрашивали! Если вы не хотите вносить это изменение, это ваше решение ... но это не делает ответ неправильным, о чем вы говорите ... и голосование против. - person Simon Dowdeswell; 29.05.2018
comment
@SimonDowdeswell - Я на самом деле не голосовал против вашего ответа, я просто в порядке любезности указал, почему это сделает кто-то другой, потому что вы заявили, что не знаете, почему кто-то это сделал. - person Gruff; 29.05.2018

Приведенное ниже работает с использованием MessageContracts, хотя и является «хакерским» - он приписывает элемент «MyClass» члену List и исключает пространство имен оболочки для «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;
    }
}
person StuartLC    schedule 21.12.2011