Можно ли сериализовать объединение, подобное C # struct, в XML? - PullRequest
2 голосов
/ 15 марта 2011

Допустим, у меня есть этот простой (объединение как) C # struct

[StructLayout(LayoutKind.Explicit)]
public struct MyData
{
    [FieldOffset(0)]
    public int Num;
    [FieldOffset(0)]
    public int Number;
    [FieldOffset(4)]
    public string Name;
    [FieldOffset(4)]
    public string Url;
};

И метод сохранения, который использует XmlSerializer и StreamWriter

static void SaveToXml(object obj, string fileName)
{
    XmlSerializer writer = new XmlSerializer(obj.GetType());
    using (StreamWriter file = new StreamWriter(fileName))
    {
        writer.Serialize(file, obj);
    }
}

Итак, если бы мы поместили некоторые данные и сохранили их:

MyData md = new MyData();
md.Name = "Ilan_01";
md.Num = 1;
SaveToXml(md, @"C:\temp\data.xml");

Файл XML будет выглядеть так:

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Num>1</Num>
  <Number>1</Number>
  <Name>Ilan_01</Name>
  <Url>Ilan_01</Url>
</MyData>

Можно ли сделать так, чтобы это выглядело (используя тот же или аналогичный метод) ??

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Num>1</Num>
  <Name>Ilan_01</Name>
</MyData>

EDIT

С другой стороны, если бы мы установили эти данные:

md = new MyData();
md.Url = "127.0.0.1";
md.Number = 2;

Мне бы хотелось, чтобы этот XML вышел.

<?xml version="1.0" encoding="utf-8"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Number>2</Number>
  <Url>127.0.0.1</Url>
</MyData>

Так что атрибут XmlIgnore не то, что я ищу.

Это просто простой пример, реальная реализация - с разными типами (одинакового размера) других структур.

Конец редактирования

Спасибо, Илан

Ответы [ 6 ]

1 голос
/ 15 марта 2011

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

public struct MyData
{
    public int Num;
    public int Number;
    public string Name;
    public string Url;
};

class XMLIgnore
{
    static void SaveToXml(MyData obj)
    {
        XmlSerializer writer2 = customserialiser(obj);
        writer2.Serialize(Console.Out, obj);
    }

    static public XmlSerializer customserialiser(MyData d)
    {
        XmlAttributes attrs = new XmlAttributes();
        attrs.XmlIgnore = true;
        XmlAttributeOverrides xmlOveride = new XmlAttributeOverrides();

        if( d.Name.Length != 0 )
            xmlOveride.Add(typeof(MyData), "Url", attrs);
        else
            xmlOveride.Add(typeof(MyData), "Name", attrs);

        if (d.Num != 0)
            xmlOveride.Add(typeof(MyData), "Number", attrs);
        else
            xmlOveride.Add(typeof(MyData), "Num", attrs);

        return new XmlSerializer(typeof(MyData), xmlOveride);
    }

    public static void go()
    {
        MyData d = new MyData();
        d.Num = 1;
        d.Number = 2;
        d.Name = "John";
        d.Url = "Happy";
        SaveToXml(d);

        Console.WriteLine();
        Console.WriteLine();

        MyData d2 = new MyData();
        d2.Num = 0;
        d2.Number = 2;
        d2.Name = "";
        d2.Url = "Happy";
        SaveToXml(d2);
    }
}
1 голос
/ 15 марта 2011

Это невозможно.Структура не будет помнить, если вы использовали Url или Name для присвоения значения, и из-за этого Serializer тоже не будет знать.

Лучший способ, которым я могу придумать, чтобы имитировать такое поведение, это разоблачитьэти поля как свойства и при их настройке запоминают, какое свойство использовалось для назначения (что требует некоторого дополнительного хранения).Затем вам придется реализовать IXmlSerializable и предоставить собственную сериализацию, которая записывает XML, в зависимости от свойств, которые вы использовали для установки данных.

Я думаю, что лучше игнорировать дублирующиеся поля, используя XmlIgnoreили использовать разные структуры для имен и URL.

0 голосов
/ 17 марта 2011

Спасибо, Патрик

Вот мой скорректированный пример:

public struct SimpleStruct1
{
    public int Number;
    public byte Channel;
}
public struct SimpleStruct2
{
    public int ID;
    public byte Mode;
}
public enum StructType
{
    ST_1=0, ST_2, 
}
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct MyData
{
    [FieldOffset(0)]
    public int ID;
    [FieldOffset(4)]
    public StructType structType;
    [FieldOffset(8)]
    [XmlElement(ElementName="SimpleStruct1")]
    public SimpleStruct1 ss1;
    [FieldOffset(8)]
    [XmlElement(ElementName = "SimpleStruct2")]
    public SimpleStruct2 ss2;
};

public class XMLIgnore
{

    static public XmlSerializer customserialiser(MyData d)
    {
        XmlAttributes attrs = new XmlAttributes();
        attrs.XmlIgnore = true;
        XmlAttributeOverrides xmlOveride = new XmlAttributeOverrides();

        switch (d.structType)
        {
            case StructType.ST_1:
                xmlOveride.Add(typeof(MyData), "ss2", attrs);
                break;
            case StructType.ST_2:
                xmlOveride.Add(typeof(MyData), "ss1", attrs);
                break;
            default:
                break;
        }

        return new XmlSerializer(typeof(MyData), xmlOveride);
    }
}
    static void SaveToXml(object obj, string fileName, XmlSerializer writer)
    {
        using (StreamWriter file = new StreamWriter(fileName))
        {
            writer.Serialize(file, obj);
        }
    }

    static void Main(string[] args)
    {
        SimpleStruct1 sStrct1 = new SimpleStruct1();
        sStrct1.Channel = 15;
        sStrct1.Number = 35;
        MyData md1 = new MyData();
        md1.ID = 1;
        md1.structType = StructType.ST_1;
        md1.ss1 = sStrct1;

        XmlSerializer writer = XMLIgnore.customserialiser(md1);
        SaveToXml(md1, @"C:\temp\dataOne.xml", writer);

        SimpleStruct2 sStrct2 = new SimpleStruct2();
        sStrct2.ID = 74;
        sStrct2.Mode = 2;
        MyData md2 = new MyData();
        md2.ID = 2;
        md2.structType = StructType.ST_2;
        md2.ss2 = sStrct2;

        writer = XMLIgnore.customserialiser(md2);
        SaveToXml(md2, @"C:\temp\dataTwo.xml", writer);

    }
0 голосов
/ 15 марта 2011

Вы можете использовать атрибут XmlIgnore для свойств, которые не хотите сериализовать.

0 голосов
/ 15 марта 2011

Просто используйте атрибут [XmlIgnore] в полях, которые вы не хотите сериализовать, например:

[StructLayout(LayoutKind.Explicit)]
public struct MyData
{
    [FieldOffset(0)]
    public int Num;
    [FieldOffset(0)]
    [XmlIgnore]
    public int Number;
    [FieldOffset(4)]
    public string Name;
    [FieldOffset(4)]
    [XmlIgnore]
    public string Url;
};
0 голосов
/ 15 марта 2011

Вы пытались добавить атрибут [NonSerialized] в поля, которые вам не нужны?

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