Сериализация в XML через DataContract: пользовательский вывод? - PullRequest
5 голосов
/ 26 октября 2010

У меня есть собственный класс Fraction, который я использую на протяжении всего проекта.Все просто, он состоит из одного конструктора, принимает два целых и сохраняет их.Я хотел бы использовать DataContractSerializer для сериализации моих объектов, используемых в моем проекте, некоторые из которых включают в себя фракции в качестве полей.В идеале я хотел бы иметь возможность сериализовать такие объекты, как это:

<Object>
    ...
    <Frac>1/2</Frac> // "1/2" would get converted back into a Fraction on deserialization.
    ...
</Object>

В противоположность этому:

<Object>
    ...
    <Frac>
        <Numerator>1</Numerator>
        <Denominator>2</Denominator>
    </Frac>
    ...
</Object>

Есть ли способ сделать это, используяDataContracts?

Я хотел бы сделать это, потому что я планирую сделать файлы XML доступными для редактирования пользователем (я использую их в качестве входных данных для музыкальной игры, и они, по сути, действуют как блокноты)и хотят, чтобы обозначение было как можно более кратким для конечного пользователя, чтобы им не приходилось иметь дело с таким количеством стен текста.

РЕДАКТИРОВАТЬ : Я также должен отметить, чтоВ настоящее время мой класс Fraction неизменен (все поля readonly), поэтому невозможно изменить состояние существующей фракции.Впрочем, возвращать новый объект Fraction было бы нормально.

Ответы [ 5 ]

6 голосов
/ 26 октября 2010

Если вы добавите свойство, которое представляет элемент Frac, и примените к нему атрибут DataMember, а не другие свойства, вы получите то, что вы хотите, я считаю:

[DataContract]
public class MyObject {
    Int32 _Numerator;
    Int32 _Denominator;
    public MyObject(Int32 numerator, Int32 denominator) {
        _Numerator = numerator;
        _Denominator = denominator;
    }
    public Int32 Numerator {
        get { return _Numerator; }
        set { _Numerator = value; }
    }
    public Int32 Denominator {
        get { return _Denominator; }
        set { _Denominator = value; }
    }
    [DataMember(Name="Frac")]
    public String Fraction {
        get { return _Numerator + "/" + _Denominator; }
        set {
            String[] parts = value.Split(new char[] { '/' });
            _Numerator = Int32.Parse(parts[0]);
            _Denominator = Int32.Parse(parts[1]);
        }
    }
}
5 голосов
/ 26 октября 2010

DataContractSerializer будет использовать пользовательский IXmlSerializable, если он указан вместо DataContractAttribute.Это позволит вам настроить форматирование XML так, как вам нужно ... но вам придется вручную кодировать процесс сериализации и десериализации для вашего класса.

public class Fraction: IXmlSerializable 
{
    private Fraction()
    {
    }
    public Fraction(int numerator, int denominator)
    {
        this.Numerator = numerator;
        this.Denominator = denominator;
    }
    public int Numerator { get; private set; }
    public int Denominator { get; private set; }

    public XmlSchema GetSchema()
    {
        throw new NotImplementedException();
    }

    public void ReadXml(XmlReader reader)
    {
        var content = reader.ReadInnerXml();
        var parts = content.Split('/');
        Numerator = int.Parse(parts[0]);
        Denominator = int.Parse(parts[1]);
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteRaw(this.ToString());
    }

    public override string ToString()
    {
        return string.Format("{0}/{1}", Numerator, Denominator);
    }
}
[DataContract(Name = "Object", Namespace="")]
public class MyObject
{
    [DataMember]
    public Fraction Frac { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var myobject = new MyObject
        {
            Frac = new Fraction(1, 2)
        };

        var dcs = new DataContractSerializer(typeof(MyObject));

        string xml = null;
        using (var ms = new MemoryStream())
        {
            dcs.WriteObject(ms, myobject);
            xml = Encoding.UTF8.GetString(ms.ToArray());
            Console.WriteLine(xml);
            // <Object><Frac>1/2</Frac></Object>
        }

        using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            ms.Position = 0;
            var obj = dcs.ReadObject(ms) as MyObject;

            Console.WriteLine(obj.Frac);
            // 1/2
        }
    }
}
3 голосов
/ 26 апреля 2013

Эта статья MSDN описывает IDataContractSurrogate Интерфейс, который:

Предоставляет методы, необходимые для замены одного типа другим DataContractSerializer во время сериализации, десериализации и экспорт и импорт документов схемы XML.

Хотя слишком поздно, все же может кому-то помочь. На самом деле, позволяет изменить XML для ЛЮБОГО класса.

1 голос
/ 28 октября 2010

Вы можете сделать это с помощью DataContractSerializer, хотя мне это кажется неприятным.Вы можете воспользоваться тем фактом, что члены данных могут быть частными переменными, и использовать личную строку в качестве вашего сериализованного члена.Сериализатор контракта данных также будет выполнять методы в определенных точках процесса, которые отмечены атрибутами [On (De) Serializ (ed | ing)]) - внутри них вы можете контролировать, как поля int отображаются в строку, инаоборот.Недостатком является то, что вы теряете магию автоматической сериализации DataContractSerializer в своем классе, и теперь у вас больше логики для поддержки.

В любом случае, вот что я бы сделал:

[DataContract]
public class Fraction
{
    [DataMember(Name = "Frac")]
    private string serialized;

    public int Numerator { get; private set; }
    public int Denominator { get; private set; }

    [OnSerializing]
    public void OnSerializing(StreamingContext context)
    {
        // This gets called just before the DataContractSerializer begins.
        serialized = Numerator.ToString() + "/" + Denominator.ToString();
    }

    [OnDeserialized]
    public void OnDeserialized(StreamingContext context)
    {
        // This gets called after the DataContractSerializer finishes its work
        var nums = serialized.Split("/");
        Numerator = int.Parse(nums[0]);
        Denominator = int.Parse(nums[1]);
    }
}
0 голосов
/ 26 октября 2010

Вам придется переключиться обратно на XMLSerializer, чтобы сделать это.DataContractSerializer немного более ограничен в плане возможности настройки вывода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...