Would it be possible to serialize a union like C# struct to XML?
Lets say I have this simple (union like) 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;
};
And a save method that uses XmlSerializer and StreamWriter
static void SaveToXml(object obj, string fileName)
{
XmlSerializer writer = new XmlSerializer(obj.GetType());
using (StreamWriter file = new StreamWriter(fileName))
{
writer.Serialize(file, obj);
}
}
So if we'd put some data in and save it:
MyData md = new MyData();
md.Name = "Ilan_01";
md.Num = 1;
SaveToXml(md, @"C:\temp\data.xml");
XML File would look like this:
<?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>
Would it be possible to make it look like this (using the same or similar method)??
<?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
On the other hand if we'd set this data:
md = new MyData();
md.Url = "127.0.0.1";
md.Number = 2;
I'd like to see this XML as out come.
<?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>
So the XmlIgnore
attr开发者_如何学运维ibute isn't what I'm looking for.
This is just a simple example, the real implementation is with different types (same size) of other structs.
End Edit
Thanks, Ilan
This isn't exactly what you want but may be expandable to meet your needs, the original code came from somewhere on msdn, can't remember where I'm afraid. I'm sure there must be a more elegant way of doing this (i.e. have a custom attribute on members of MyData) but I don't know it:
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);
}
}
That is not possible. The structure won't remember if you used Url or Name to assign a value and because of that, the Serializer won't know either.
The best way I can think of, to mimic that behaviour is to expose those fields as properties and when setting them remember which property was used for assignment (which requires some additional storage). You would then have to implement IXmlSerializable
and provide your own Serialization that writes the XML, depending on the properties you used to set the data.
I think it is better to ignore the duplicate fields using XmlIgnore
or to use different structures for names and URLs.
Have you tried adding the [NonSerialized] attribute to the fields you dont want?
Just use the [XmlIgnore]
attribute on the fields you don't want serialized, like this:
[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;
};
You could use the XmlIgnore
attribute on properties you don't want to serialize.
Thanks Patrick
Here is my adjusted example:
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);
}
精彩评论