I have this XML format I need to replicate:
<item>
<attribute1>1</attribute1>
<attribute2 something="true">
2
</attribute2>
<attribute3 something="false">
3
</attribute3>
<!-- goes on until attribute25 -->
</item>
I'm currently using something like this to achieve what I want:
Item.cs:
[XmlType(TypeName = "item")]
public class Item {
[XmlElement("attribute1")]
public CustomElement<string> Attribute1 { get; set; }
[XmlElement("attribute2")]
public CustomElement<string> Attribute2 { get; set; }
[XmlElement("attribute3")]
public CustomElement<string> Attribute3 { get; set; }
// Etc Etc
}
CustomElement.cs:
/// <summary>
/// Represents a CustomElement.
/// </summary>
/// <typeparam name="T">The type for the value of the element.</typeparam>
public class CustomElement<T>
{
[XmlIgnore] public T Value;
[XmlText]
public T XmlValue => Value;
public CustomElement()
{
}
public CustomElement(T value)
{
Value = value;
}
[XmlIgnore]
public bool? Something { get; set; }
[XmlAttribute("something")]
public bool XmlSomething
{
get => Something != null && Something.Value;
set => Something = value;
}
public bool XmlSomethingSpecified => Something.HasValue;
public static implicit operator CustomElement<T>(T x)
{
return new CustomElement<T>(x);
}
}
However, after serializing my Item
I get:
<item>
<attribute1 />
<attribute2 />
<attribute3 />
</item>
How do I fix my CustomElement
class so the value doesn't get lost?
You have several problems here:
You are trying to serialize the XmlValue
member as the element value for the element generated for an instance of CustomElement<T>
, but you have defined it as a read-only expression-bodied member:
public T XmlValue => Value;
XmlSerializer
will only serialize public read/write properties and fields, so you must add a setter as well as a getter:
[XmlText]
public T XmlValue { get => Value; set => Value = value; }
Alternatively, to simplify your model you could eliminate XmlValue
entirely and serialize Value
directly by marking it with [XmlText]
. XML serialization attributes can be applied to fields as well as properties.
You are trying to suppress serialization of the <something>
element when the underlying value is null
using the {propertyName}Specified
pattern. This pattern is primarily used for tracking whether or not an associated element is encountered, and so the xxxSpecified
property must be marked with [XmlIgnore]
:
[XmlIgnore]
public bool XmlSomethingSpecified => Something.HasValue;
Or, since you don't actually need to track the presence of the <something>
element, you could simplify your model by switching to the ShouldSerialize{PropertyName}()
pattern:
public bool ShouldSerializeXmlSomething() => Something.HasValue;
The differences between the two patterns are described in ShouldSerialize() vs Specified Conditional Serialization Pattern.
If your Item
type is going to be the root element of your XML document, you must mark it with [XmlRoot("item")]
to make the root element's name be <item>
:
[XmlRoot("item")]
[XmlType(TypeName = "ci")]
public class Item
{
// Etc Etc
}
In your c# code you have marked the XmlSomething
property with [XmlElement]
but in your XML something
is shown as an element: <something>true</something>
.
If you really want it to be an element you must mark XmlSomething
with [XmlElement]
:
[XmlElement("something")]
public bool XmlSomething
{
get => Something != null && Something.Value;
set => Something = value;
}
In your question, you show an example of a <something>
element with a non-boolean textual value:
<attribute3>
3
<something>text</something>
</attribute3>
I reckon this is a typo in the question, but if not, you will need to redesign your CustomElement<T>
type to make Something
be a string
.
Sample working Roslyn .Net fiddle, and a second with the suggested simplifications for CustomElement<T>
, and a third where <something>
appears as a child element.
The classes from the second fiddle look like:
public class CustomElement<T>
{
[XmlText]
public T Value;
public CustomElement()
{
}
public CustomElement(T value)
{
Value = value;
}
[XmlIgnore]
public bool? Something { get; set; }
[XmlAttribute("something")]
public bool XmlSomething
{
get => Something != null && Something.Value;
set => Something = value;
}
public bool ShouldSerializeXmlSomething() => Something.HasValue;
public static implicit operator CustomElement<T>(T x)
{
return new CustomElement<T>(x);
}
}
[XmlRoot("item")]
[XmlType(TypeName = "ci")]
public class Item
{
[XmlElement("attribute1")]
public CustomElement<string> Attribute1 { get; set; }
[XmlElement("attribute2")]
public CustomElement<string> Attribute2 { get; set; }
[XmlElement("attribute3")]
public CustomElement<string> Attribute3 { get; set; }
// Etc Etc
}