2017-06-20 4 views
1

Je suis confronté à un problème avec le .Net XmlSerializer, fondamentalement j'ai besoin d'un ou plusieurs éléments du même nom à sérialiser (et désérialiser) dynamiquement entre les autres éléments du schéma fixe.XmlSerialization avec XmlAnyElement à la propriété XmlNode et Order

Exemple:

<?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> 

Mon vrai tag <nnn> est un peu plus compliqué, avec des balises dynamiques à l'intérieur (non seulement) conditionals, mais je threating actuellement ce droit. J'ai vraiment besoin d'utiliser le paramètre "Order" de XmlElement pour contrôler certaines règles.

Je ne peux pas modifier la disposition XML.

L'exemple classe sérialisable:

[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; } 

Le problème est, quand ne pas utiliser le « FSMA » paramètre de la sérialisation va bien, mais avec le paramètre les éléments après la XmlAnyElement sont compris dans le cadre du réseau de nœuds et ne sont donc pas désérialisés à droite.

Mon programme principal pour l'exemple est une application console avec les principaux éléments suivants:

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(); 
} 

La sortie mal est:

<?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; 

Pour les tests, voici la sérialisation/désérialisation extraits que j'utilise:

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); 
    } 
} 

Le code source complet: https://gist.github.com/inventti-gabriel/81054269f2e0a32d7e8d1dd44f30a97f

Merci d'avance.

+2

Cela ressemble à un bug pour moi. Pouvez-vous donner plus de détails sur ce qu'est vraiment «nnn»? Parce que vous pourriez [changer à ceci] (https://dotnetfiddle.net/E2b4xp) pour l'exemple, mais cela peut ne pas fonctionner dans votre "vrai" cas. En aparté, notez dans l'exemple que vous pouvez simplifier vos méthodes sérialiser/désérialiser pour utiliser 'StringWriter' /' StringReader'. –

Répondre

1

Je suis d'accord avec @CharlesMager que cela ne semble pas être un bogue dans XmlSerializer.

Cela étant dit, vous pouvez contourner le bug en introduisant une classe d'emballage intermédiaire ou struct pour contenir les noeuds arbitraires, modifiant alors votre mère porteuse nnn propriété pour retourner un tableau de ces struct après avoir marqué la propriété avec [XmlElement("nnn", Order = 2)]:

[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)); 
    } 
} 

Notez que la propriété Nodes dans XmlNodeWrapper est marquée à la fois [XmlAnyElement] et [XmlText]. L'exigence de le faire est expliquée here.

Echantillon fiddle.

0

Essayez d'utiliser XmlArrayAttribute à la place de XmlAnyElement comme les noeuds sont ensemble

+0

J'ai essayé, mais cela casse la structure du XML – gariel