I have this snippet of XML (actually it's XBRL, but that is based on XML)
<xbrl>
<context id="Context_Duration" />
<context id="Context_Instant_Begin" />
<context id="Context_Instant_End" />
<ConstructionDepotSpecification>
<ConstructionDepotLabel contextRef="Context_Duration">depot</ConstructionDepotLabel>
<ConstructionDepotBalance contextRef="Context_Instant_Begin">45000</ConstructionDepotBalance>
<ConstructionDepotBalance contextRef="Context_Instant_End">42000</ConstructionDepotBalance>
</ConstructionDepotSpecification>
</xbrl>
(additional content and xml namespaces declarations removed for clarity)
I want to deserialize this to a class, but I'm not sure how to handle the ConstructionDepotBalance elements. If I define a property ConstructionDepotBalance it will just take the value of the first element, so I think I should create two properties instead, one for begin value and one for end value. So the class should look like this
[XmlRoot(ElementName = "xbrl")]
public partial class Taxonomy
{
[XmlElement]
public List<ConstructionDepotSpecification> ConstructionDepotSpecification { get; set; }
}
public partial class ConstructionDepotSpecification
{
public string ConstructionDepotLabel { get; set; }
public long? ConstructionDepotBalanceBegin { get; set; }
public long? ConstructionDepotBalanceEnd { get; set; }
}
So the element with attribute Context_Instant_Begin should be deserialized to ConstructionDepotBalanceBegin and the other element with attribute Context_Instant_End should be deserialized to ConstructionDepotBalanceEnd.
Is this possible to achieve? Should I use an IXmlSerializable implementation for this?
CodePudding user response:
My first approach:
You could parse the XML-String first and replace
[ConstructionDepotBalance contextRef="Context_Instant_Begin"]
with
[ConstructionDepotBalanceBegin]
(Same with ConstructionDepotBalanceEnd
).
In the second step you deserialze the XML string.
CodePudding user response:
I experimented a bit with the IXmlSerializable interface and came up with this implementation:
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement();
while (!reader.EOF)
{
var ctx = reader.GetAttribute("contextRef");
if (ctx == "Context_Duration")
{
string propName = reader.Name;
var propInfo = GetType().GetProperty(propName);
Type propType = propInfo.PropertyType;
if (propType.GenericTypeArguments.Length > 0)
{
propType = propType.GenericTypeArguments[0];
}
var value = reader.ReadElementContentAs(propType, null);
propInfo.SetValue(this, value);
}
else if (ctx == "Context_Instant_Begin")
{
string propName = reader.Name "Begin";
var propInfo = GetType().GetProperty(propName);
var value = reader.ReadElementContentAsLong();
propInfo.SetValue(this, value);
}
else if (ctx == "Context_Instant_End")
{
string propName = reader.Name "End";
var propInfo = GetType().GetProperty(propName);
var value = reader.ReadElementContentAsLong();
propInfo.SetValue(this, value);
}
if (reader.NodeType == XmlNodeType.EndElement)
{
reader.ReadEndElement();
break;
}
}
}
Not sure if it's the best solution for this problem, but for now it does what I want.