Ich erhalte eine XML-Datei von einem Drittanbieter und muss sie in ein C # -Objekt deserialisieren. Diese XML-Datei kann Attribute mit einem Wert vom Typ Integer oder einem leeren Wert enthalten: attr = ”11” oder attr = ””. Ich möchte diesen Attributwert in die Eigenschaft mit dem Typ einer nullbaren Ganzzahl deserialisieren. XmlSerializer unterstützt jedoch keine Deserialisierung in nullfähige Typen. Der folgende Testcode schlägt beim Erstellen von XmlSerializer mit InvalidOperationException fehl {"Es ist ein Fehler aufgetreten, der den Typ 'TestConsoleApplication.SerializeMe' widerspiegelt."}.
[XmlRoot("root")]
public class SerializeMe
{
[XmlElement("element")]
public Element Element { get; set; }
}
public class Element
{
[XmlAttribute("attr")]
public int? Value { get; set; }
}
class Program {
static void Main(string[] args) {
string xml = "<root><element attr=''>valE</element></root>";
var deserializer = new XmlSerializer(typeof(SerializeMe));
Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
var result = (SerializeMe)deserializer.Deserialize(xmlStream);
}
}
Wenn ich den Typ der 'Value'-Eigenschaft in int ändere, schlägt die Deserialisierung mit InvalidOperationException fehl:
Im XML-Dokument (1, 16) ist ein Fehler aufgetreten.
Kann jemand raten, wie man ein Attribut mit einem leeren Wert in einen nullbaren Typ (als Null) deserialisiert und gleichzeitig einen nicht leeren Attributwert in eine Ganzzahl deserialisiert? Gibt es dafür einen Trick, damit ich nicht jedes Feld manuell deserialisieren muss (tatsächlich gibt es viele davon)?
Update nach Kommentar von ahsteele:
-
Soweit ich weiß, funktioniert dieses Attribut nur mit XmlElementAttribute - dieses Attribut gibt an, dass das Element keinen Inhalt hat, egal ob untergeordnete Elemente oder Textkörper. Aber ich muss die Lösung für XmlAttributeAttribute finden. Auf jeden Fall kann ich XML nicht ändern, da ich keine Kontrolle darüber habe.
-
Diese Eigenschaft funktioniert nur, wenn der Attributwert nicht leer ist oder wenn das Attribut fehlt. Wenn attr einen leeren Wert hat (attr = ''), schlägt der XmlSerializer-Konstruktor fehl (wie erwartet).
public class Element { [XmlAttribute("attr")] public int Value { get; set; } [XmlIgnore] public bool ValueSpecified; }
Benutzerdefinierte Nullable-Klasse wie in diesem Blog-Beitrag von Alex Scordellis
Ich habe versucht, die Klasse aus diesem Blog-Beitrag auf mein Problem zu übertragen:
[XmlAttribute("attr")] public NullableInt Value { get; set; }
Der XmlSerializer-Konstruktor schlägt jedoch mit InvalidOperationException fehl:
Mitglied 'Wert' vom Typ TestConsoleApplication.NullableInt kann nicht serialisiert werden.
XmlAttribute / XmlText kann nicht zum Codieren von Typen verwendet werden, die IXmlSerializable implementieren.}
Hässliche Ersatzlösung (schade, dass ich diesen Code hier geschrieben habe :)):
public class Element { [XmlAttribute("attr")] public string SetValue { get; set; } public int? GetValue() { if ( string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0 ) return null; int result; if (int.TryParse(SetValue, out result)) return result; return null; } }
Aber ich möchte nicht auf diese Lösung kommen, weil sie die Schnittstelle meiner Klasse für ihre Verbraucher bricht. Ich würde besser IXmlSerializable Schnittstelle manuell implementieren.
Derzeit muss ich IXmlSerializable für die gesamte Element-Klasse implementieren (es ist groß) und es gibt keine einfache Problemumgehung…
quelle
Ich habe dieses Problem durch die Implementierung der IXmlSerializable-Schnittstelle gelöst. Einfacheren Weg habe ich nicht gefunden.
Hier ist das Testcodebeispiel:
[XmlRoot("root")] public class DeserializeMe { [XmlArray("elements"), XmlArrayItem("element")] public List<Element> Element { get; set; } } public class Element : IXmlSerializable { public int? Value1 { get; private set; } public float? Value2 { get; private set; } public void ReadXml(XmlReader reader) { string attr1 = reader.GetAttribute("attr"); string attr2 = reader.GetAttribute("attr2"); reader.Read(); Value1 = ConvertToNullable<int>(attr1); Value2 = ConvertToNullable<float>(attr2); } private static T? ConvertToNullable<T>(string inputValue) where T : struct { if ( string.IsNullOrEmpty(inputValue) || inputValue.Trim().Length == 0 ) { return null; } try { TypeConverter conv = TypeDescriptor.GetConverter(typeof(T)); return (T)conv.ConvertFrom(inputValue); } catch ( NotSupportedException ) { // The conversion cannot be performed return null; } } public XmlSchema GetSchema() { return null; } public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } } class TestProgram { public static void Main(string[] args) { string xml = @"<root><elements><element attr='11' attr2='11.3'/><element attr='' attr2=''/></elements></root>"; XmlSerializer deserializer = new XmlSerializer(typeof(DeserializeMe)); Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml)); var result = (DeserializeMe)deserializer.Deserialize(xmlStream); } }
quelle
Ich habe in letzter Zeit selbst viel mit der Serialisierung herumgespielt und fand die folgenden Artikel und Beiträge hilfreich, wenn es um Nulldaten für Werttypen geht.
Die Antwort auf Wie man einen Werttyp mit XmlSerializer in C # nullbar macht - Serialisierung beschreibt einen ziemlich raffinierten Trick des XmlSerializer. Insbesondere sucht der XmlSerialier nach einer XXXSpecified-Booleschen Eigenschaft, um zu bestimmen, ob sie enthalten sein soll, sodass Sie Nullen ignorieren können.
Alex Scordellis stellte eine StackOverflow-Frage, die eine gute Antwort erhielt . Alex hat in seinem Blog auch einen guten Bericht über das Problem verfasst, das er mit XmlSerializer lösen wollte, um es in ein Nullable <int> zu deserialisieren .
Die MSDN-Dokumentation zur Unterstützung der Xsi: nil-Attributbindung ist ebenfalls hilfreich. Wie die Dokumentation zu IXmlSerializable Interface , sollte das Schreiben einer eigenen Implementierung Ihr letzter Ausweg sein.
quelle
Ich dachte, ich könnte meine Antwort genauso gut in den Hut werfen: Dieses Problem wurde behoben, indem ein benutzerdefinierter Typ erstellt wurde, der die IXmlSerializable-Schnittstelle implementiert:
Angenommen, Sie haben ein XML-Objekt mit den folgenden Knoten:
<ItemOne>10</Item2> <ItemTwo />
Das Objekt, um sie darzustellen:
public class MyItems { [XmlElement("ItemOne")] public int ItemOne { get; set; } [XmlElement("ItemTwo")] public CustomNullable<int> ItemTwo { get; set; } // will throw exception if empty element and type is int }
Dynamische nullfähige Struktur zur Darstellung potenzieller nullbarer Einträge zusammen mit der Konvertierung
public struct CustomNullable<T> : IXmlSerializable where T: struct { private T value; private bool hasValue; public bool HasValue { get { return hasValue; } } public T Value { get { return value; } } private CustomNullable(T value) { this.hasValue = true; this.value = value; } public XmlSchema GetSchema() { return null; } public void ReadXml(XmlReader reader) { string strValue = reader.ReadString(); if (String.IsNullOrEmpty(strValue)) { this.hasValue = false; } else { T convertedValue = strValue.To<T>(); this.value = convertedValue; this.hasValue = true; } reader.ReadEndElement(); } public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); } public static implicit operator CustomNullable<T>(T value) { return new CustomNullable<T>(value); } } public static class ObjectExtensions { public static T To<T>(this object value) { Type t = typeof(T); // Get the type that was made nullable. Type valueType = Nullable.GetUnderlyingType(typeof(T)); if (valueType != null) { // Nullable type. if (value == null) { // you may want to do something different here. return default(T); } else { // Convert to the value type. object result = Convert.ChangeType(value, valueType); // Cast the value type to the nullable type. return (T)result; } } else { // Not nullable. return (T)Convert.ChangeType(value, typeof(T)); } } }
quelle
Sie können dies auch tun, indem Sie das
xml
in ein ladenXmlDocument
und es dann deserialisierenJson
, um das gesuchte Objekt zu erhaltenT
.public static T XmlToModel<T>(string xml) { XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); string jsonText = JsonConvert.SerializeXmlNode(doc); T result = JsonConvert.DeserializeObject<T>(jsonText); return result; }
quelle