是否可以将像C#struct这样的联合序列化为XML?

时间:2011-03-15 15:45:18

标签: c# .net xml

让我说我有这个简单的(像联盟一样)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>

修改

另一方面,如果我们设置这些数据:

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属性不是我想要的。

这只是一个简单的例子,真正的实现是使用不同类型(相同大小)的其他结构。

结束修改

谢谢,Ilan

6 个答案:

答案 0 :(得分:1)

这是不可能的。如果您使用Url或Name来分配值,结构将不会记住,因此,Serializer也不会知道。

我能想到的最佳方式是,模仿该行为是将这些字段公开为属性,并在设置它们时记住哪个属性用于赋值(这需要一些额外的存储空间)。然后,您必须实现IXmlSerializable并提供自己的编写XML的序列化,具体取决于您用于设置数据的属性。

我认为最好使用XmlIgnore忽略重复字段,或者为名称和网址使用不同的结构。

答案 1 :(得分:1)

这不是你想要的,但可能是可扩展的,以满足你的需求,原始代码来自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);
    }
}

答案 2 :(得分:0)

您是否尝试将[NonSerialized]属性添加到您不想要的字段?

答案 3 :(得分:0)

只需在您不希望序列化的字段上使用[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;
};

答案 4 :(得分:0)

您可以在不想序列化的属性上使用XmlIgnore属性。

答案 5 :(得分:0)

谢谢Patrick

以下是我调整后的例子:

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

    }