I have a bunch of C# classes, which are auto generated from an XSD. Then I generate XML files based on those C# classes. Nothing existing so far.
The proble
XmlSerializer
doesn't directly support binding to elements that simultaneously have xsi:nil="true"
along with other attribute values; see Xsi:nil Attribute Binding Support: The nil attribute and other attributes.
Thus, you need to emit the attribute manually.
If you want to be able to generate an element with no content and two attributes, one named NV
and the other always being xsi:nil="true"
, you can modify your testTag01
class to have the NV
property as well as a synthetic property having the correct namespace and name:
public class testTag01
{
[XmlAttribute]
public string NV { get; set; }
[XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Nil { get { return "true"; } set { } }
}
If you sometimes want to have xsi:nil="true"
but at other times want the element to have content corresponding to your SomeEnum
, you need to do something a bit more complicated, since the xsi:nil="true"
must be suppressed when the element has content:
public class testTag01
{
[XmlAttribute]
public string NV { get; set; }
[XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Nil { get { return SomeEnum == null ? "true" : null; } set { } }
public bool ShouldSerializeNil() { return SomeEnum == null; }
[XmlIgnore]
public SomeEnum? SomeEnum { get; set; }
[XmlText]
public string SomeEnumText
{
get
{
if (SomeEnum == null)
return null;
return SomeEnum.Value.ToString();
}
set
{
// See here if one needs to parse XmlEnumAttribute attributes
// http://stackoverflow.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value
value = value.Trim();
if (string.IsNullOrEmpty(value))
SomeEnum = null;
else
{
try
{
SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, false);
}
catch (Exception)
{
SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, true);
}
}
}
}
}
(An element that simultaneously has both xsi:nil="true"
and content would be a violation of the XML standard; hopefully you don't have that.)
Then use it like:
public class TestClass
{
[XmlElement("testTag.01")]
public testTag01 TestTag { get; set; }
public static void Test()
{
Test(new TestClass { TestTag = new testTag01 { NV = "123123" } });
Test(new TestClass { TestTag = new testTag01 { NV = "123123", SomeEnum = SomeEnum.SomeValue } });
}
private static void Test(TestClass test)
{
var xml = test.GetXml();
var test2 = xml.LoadFromXML();
Console.WriteLine(test2.GetXml());
Debug.WriteLine(test2.GetXml());
if (test2.TestTag.NV != test.TestTag.NV)
{
throw new InvalidOperationException("test2.TestTag.NV != test.TestTag.NV");
}
}
}
The XML output looks like:
Or
SomeValue
Prototype fiddle using these extension methods:
public static class XmlSerializationHelper
{
public static T LoadFromXML(this string xmlString, XmlSerializer serializer = null)
{
T returnValue = default(T);
using (StringReader reader = new StringReader(xmlString))
{
object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
if (result is T)
{
returnValue = (T)result;
}
}
return returnValue;
}
public static string GetXml(this T obj, XmlSerializerNamespaces ns = null, XmlWriterSettings settings = null, XmlSerializer serializer = null)
{
using (var textWriter = new StringWriter())
{
settings = settings ?? new XmlWriterSettings() { Indent = true, IndentChars = " " }; // For cosmetic purposes.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
(serializer ?? new XmlSerializer(typeof(T))).Serialize(xmlWriter, obj, ns);
return textWriter.ToString();
}
}
}