2017-06-20 1 views
1

Ich bin mit einem Problem mit der .Net XmlSerializer konfrontiert, im Grunde brauche ich ein oder mehrere Elemente mit dem gleichen Namen dynamisch zwischen anderen Elementen des festen Schemas serialisiert (und deserialisiert).XmlSerialization mit XmlAnyElement XmlNode und Order-Eigenschaft

Beispiel:

<?xml version="1.0" encoding="utf-8"?> 
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <asd>asd</asd> 
    <nnn>q</nnn> 
    <nnn>w</nnn> 
    <nnn>e</nnn> 
    <aaa>aaa</aaa> 
</A> 

Mein wirklicher <nnn> Tag ist ein wenig komplizierter, mit dynamischen Tags innen (nicht nur conditionals), aber ich bin zur Zeit dieses Recht threating. Ich brauche wirklich den "Order" -Parameter von XmlElement, um einige Regeln zu steuern.

Ich kann das XML-Layout nicht ändern.

Das Beispiel serializable Klasse:

[XmlRoot] 
[Serializable] 
public class A 
{ 
    [XmlElement("asd", Order=1)] 
    public string asd { get; set; } 

    [XmlIgnore] 
    public string[] qwe { get; set; } 

    [XmlAnyElement("nnn", Order=2)] 
    public XmlNode[] nnn 
    { 
     get 
     { 
      if (qwe == null) return null; 

      var xml = new XmlDocument(); 
      var nodes = new List<XmlNode>(qwe.Length); 

      foreach (var q in qwe) 
      { 
       var nnnTag = xml.CreateNode(XmlNodeType.Element, "nnn", null); 
       nnnTag.InnerText = q; 
       nodes.Add(nnnTag); 
      } 

      return nodes.ToArray(); 
     } 
     set 
     { 
      if (value == null) return; 
      qwe = value.Select(tag => tag.InnerText).ToArray(); 
     } 
    } 

    [XmlElement("aaa", Order=3)] 
    public string aaa { get; set; } 

Das Problem ist, wenn nicht die „Order“ mit dem Parameter der Serialisierung geht in Ordnung, aber mit dem Parameter der Elemente nach den XmlAnyElement werden als Teil des Arrays verstanden von Knoten und so sind nicht richtig deserialisiert.

Mein Hauptprogramm für das Beispiel ist eine Konsole-Anwendung mit der folgenden Haupt:

static void Main(string[] args) 
{ 
    var a = new A 
    { 
     aaa = "aaa", 
     asd = "asd", 
     qwe = new[] {"q", "w", "e"} 
    }; 
    var s = Serialize(a); 
    var ss = Deserialize<A>(s); 
    var s2 = Serialize(ss); 

    Console.WriteLine(s); 
    Console.WriteLine(s2); 

    Console.WriteLine("Equals: {0};", s == s2); 

    Console.ReadKey(); 
} 

Die falsche Ausgabe lautet:

<?xml version="1.0" encoding="utf-8"?> 
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <asd>asd</asd> 
    <nnn>q</nnn> 
    <nnn>w</nnn> 
    <nnn>e</nnn> 
    <aaa>aaa</aaa> 
</A> 
<?xml version="1.0" encoding="utf-8"?> 
<A xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <asd>asd</asd> 
    <nnn>q</nnn> 
    <nnn>w</nnn> 
    <nnn>e</nnn> 
    <nnn>aaa</nnn> 
</A> 
Equals: False; 

Für die Prüfung, hier ist die Serialisierung/Deserialisierung Schnipsel, die ich benutze:

public static string Serialize<T>(T a) 
{ 
    var s = new XmlSerializer(typeof(T)); 
    using (var ms = new MemoryStream()) 
    { 
     using (TextWriter sw = new StreamWriter(ms)) 
     { 
      s.Serialize(sw, a); 
      ms.Seek(0, 0); 
      using (var sr = new StreamReader(ms)) 
      { 
       return sr.ReadToEnd(); 
      } 
     } 
    } 
} 

public static T Deserialize<T>(string a) 
{ 
    var s = new XmlSerializer(typeof(T)); 
    var bytes = Encoding.ASCII.GetBytes(a); 
    using (var ms = new MemoryStream(bytes)) 
    { 
     return (T) s.Deserialize(ms); 
    } 
} 

Der vollständige Quellcode: https://gist.github.com/inventti-gabriel/81054269f2e0a32d7e8d1dd44f30a97f

Vielen Dank im Voraus.

+2

Das sieht für mich wie ein Fehler aus. Kannst du genauer sagen, was "nnn" wirklich ist? Weil du das für das Beispiel ändern könntest (https://dotnetfiddle.net/E2b4xp), aber das funktioniert vielleicht nicht in deinem "echten" Fall. Nebenbei bemerkt, im Beispiel können Sie Ihre Serialisierungs-/Deserialisierungsmethoden vereinfachen, um 'StringWriter' /' StringReader' zu verwenden. –

Antwort

1

ich mit @CharlesMager darüber einig, dass dies ein Fehler in XmlSerializer zu sein scheint.

aber sagt, dass Sie um den Fehler umgehen, indem eine Zwischen Wrapper-Klasse oder Struktur Einführung des beliebigen Knoten enthält, dann Surrogat nnn Eigenschaft Modifizieren einen Array dieser Strukturen zurückzukehren, nachdem die Immobilie mit [XmlElement("nnn", Order = 2)] Kennzeichnung:

[XmlRoot] 
[Serializable] 
public class A 
{ 
    [XmlElement("asd", Order = 1)] 
    public string asd { get; set; } 

    [XmlIgnore] 
    public string[] qwe { get; set; } 

    [XmlElement("nnn", Order = 2)] 
    public XmlNodeWrapper [] nnn 
    { 
     get 
     { 
      if (qwe == null) 
       return null; 

      var xml = new XmlDocument(); 
      var nodes = new List<XmlNode>(qwe.Length); 

      foreach (var q in qwe) 
      { 
       var nnnTag = xml.CreateNode(XmlNodeType.Element, "nnn", null); 
       nnnTag.InnerText = q; 
       nodes.Add(nnnTag); 
      } 

      return nodes.Select(n => (XmlNodeWrapper)n.ChildNodes).ToArray(); 
     } 
     set 
     { 
      if (value == null) 
       return; 
      qwe = value.Select(tag => tag.InnerText()).ToArray(); 
     } 
    } 

    [XmlElement("aaa", Order = 3)] 
    public string aaa { get; set; } 
} 

[XmlType(AnonymousType = true)] 
public struct XmlNodeWrapper 
{ 
    public static implicit operator XmlNodeWrapper(XmlNodeList nodes) 
    { 
     return new XmlNodeWrapper { Nodes = nodes == null ? null : nodes.Cast<XmlNode>().ToArray() }; 
    } 

    public static implicit operator XmlNode[](XmlNodeWrapper wrapper) 
    { 
     return wrapper.Nodes; 
    } 

    // Marking the Nodes property with both [XmlAnyElement] and [XmlText] indicates that the node array 
    // may contain mixed content (I.e. both XmlElement and XmlText objects). 
    // Hat tip: https://stackoverflow.com/questions/25995609/xmlserializer-node-containing-text-xml-text 
    [XmlAnyElement] 
    [XmlText] 
    public XmlNode[] Nodes { get; set; } 

    public string InnerText() 
    { 
     if (Nodes == null) 
      return null; 
     return String.Concat(Nodes.Select(n => n.InnerText)); 
    } 
} 

Beachten Sie, dass die Nodes Eigenschaft in XmlNodeWrapper markiert ist sowohl mit [XmlAnyElement] und [XmlText]. Die Anforderung, dies zu tun, wird erläutert here.

Probe fiddle.

0

Try XmlArrayAttribute anstelle von XmlAnyElement als Knoten verwendet, ist Array

+0

Ich habe versucht, aber das bricht die Struktur des XML – gariel

Verwandte Themen