2016-06-24 1 views
1

J'essaye de désérialiser un fichier XML avec XmlSerializer en C#.Désérialise l'élément XML avec xsi: nil = "true" dans C#

La classe de destination qui suit a été générée automatiquement à l'aide de l'utilitaire xsd.

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] 
    [System.SerializableAttribute()] 
    [System.Diagnostics.DebuggerStepThroughAttribute()] 
    [System.ComponentModel.DesignerCategoryAttribute("code")] 
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] 
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = true)] 
    public partial class location 
    { 

     private string cityField; 

     private string countryField; 

     private string stateField; 

     private string textField; 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string city 
     { 
      get 
      { 
       return this.cityField; 
      } 
      set 
      { 
       this.cityField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string country 
     { 
      get 
      { 
       return this.countryField; 
      } 
      set 
      { 
       this.countryField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string state 
     { 
      get 
      { 
       return this.stateField; 
      } 
      set 
      { 
       this.stateField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlTextAttribute()] 
     public string Text 
     { 
      get 
      { 
       return this.textField; 
      } 
      set 
      { 
       this.textField = value; 
      } 
     } 
    } 

Tout fonctionne bien jusqu'à ce que je tire cette partie du fichier:

<locations> 
    <location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/> 
    <location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/> 
</locations> 

Comme stated in the MSDN, un élément avec xsi: nil = « true » sera désérialisé comme un objet nul, perdant tous les attributs complètement. En C# cela se traduit par un objet nul.

Existe-t-il un moyen de modifier ce comportement pour avoir les trois propriétés désérialisées?

Merci d'avance pour tout conseil!

EDIT 1:

Ceci est l'espace de nommage:

<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd"> 
    (location is within here somewhere) 
</records> 
+1

Ce n'est pas bien formé xml. Le préfixe 'xsi' n'est lié à aucun espace de noms. –

+0

Il a une spécification, je l'ai simplement omis pour la vie privée des entreprises. J'ai ajouté une version modifiée. – GigiSan

+1

Cela ressemble à une copie de [Puis-je avoir un attribut null et un autre attribut sur la même balise en XML créé par la classe XSD C# générée?] (Https://stackoverflow.com/questions/32903839/can-i-have-null- attribut-et-autre-attribut-at-the-same-tag-in-xml-created-by) – dbc

Répondre

4

Si vous voulez simplement jeter l'attribut xsi:nil, définissez XmlElementAttribute.IsNullable = false dans la [XmlElement] attribut sur la propriété dans le containi ng classe qui fait référence à location. E.g. si vous avez

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")] 
public class LocationList 
{ 
    [XmlElement("location", IsNullable = true)] 
    public List<location> Locations { get; set; } 
} 

changer manuellement à:

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")] 
public class LocationList 
{ 
    [XmlElement("location", IsNullable = false)] 
    public List<location> Locations { get; set; } 
} 

Si vous voulez lier à la valeur de xsi:nil tout en se liant simultanément aux autres valeurs d'attributs, en plus de la mise en IsNullable = false, vous peut ajouter une propriété manuelle le long des lignes de celui indiqué dans Can I have null attribute and other attribute at the same tag in XML created by XSD C# generated class?:

public partial class location 
{ 
    bool nil; 

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")] 
    public bool Nil 
    { 
     get 
     { 
      return nil; 
     } 
     set 
     { 
      nil = value; 
     } 
    } 

    public bool ShouldSerializeNil() { return nil == true; } 
} 

Échantillon fiddle.

Vous pouvez également pré-et post-traiter le code XML pour renommer l'attribut xsi:nil, comme cela a été suggéré dans this answer.

+0

C'est exactement l'idée que j'ai eue et que j'ai mentionnée dans les commentaires, mais elle semble se casser quand elle rencontre un '' sans un xsi: nil, définissant null pour tous '' s suivants. – GigiSan

+0

@GigiSan - pouvez-vous modifier votre question pour inclure un [mcve] pour le problème que vous voyez maintenant? J'ai mis à jour mon [violon] (https://dotnetfiddle.net/LQOZDQ) et je ne le vois pas. – dbc

+0

désolé pour la longue attente. J'ai vérifié les changements que j'ai faits dans le code et j'ai découvert que j'avais placé 'IsNullable = false' sur la classe' Location' au lieu de la liste 'Locations' (en fait cela me semblait plus logique). J'ai aussi ajouté que la méthode 'ShouldSerializeNil()', mais je ne l'ai pas comprise clairement, est-elle une méthode standard de sérialisation? Quoi qu'il en soit, merci pour l'aide. Comme cela est similaire à ce que j'ai fait et que je savais déjà que cela pourrait fonctionner, je signale celui-ci comme une réponse. – GigiSan

2

Essayez d'utiliser XmlReader personnalisé. Cette approche peut être utile lorsqu'il n'est pas possible de modifier les classes utilisées pour la désérialisation. En outre, il ne nécessite pas de consommation supplémentaire de mémoire.

public class NilIgnoreReader : XmlTextReader 
{ 
    public NilIgnoreReader(string url) : base(url) { } 

    bool isLocation = false; 

    public override bool Read() 
    { 
     bool read = base.Read(); 

     if (NodeType == XmlNodeType.Element && LocalName == "location") 
      isLocation = true; 

     return read; 
    } 

    public override string GetAttribute(string localName, string namespaceURI) 
    { 
     if (isLocation && localName == "nil" && 
      namespaceURI == "http://www.w3.org/2001/XMLSchema-instance") 
     { 
      isLocation = false; 
      return "false"; 
     } 
     return base.GetAttribute(localName, namespaceURI); 
    } 
} 

Le NilIgnoreReader retour false pour nil attribut lié à l'espace de noms à partir des http://www.w3.org/2001/XMLSchema-instancelocation éléments.

Utilisation

var xs = new XmlSerializer(typeof(Records)); 
Records record; 

using (var reader = new NilIgnoreReader("test.xml")) 
    record = (Records)xs.Deserialize(reader); 

Il travaille pour XML comme comme

<?xml version="1.0"?> 
<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd"> 
    <locations> 
    <location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/> 
    <location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/> 
    </locations> 
</records> 

et les classes comme comme

[XmlRoot("records")] 
public class Records 
{ 
    [XmlArray("locations")] 
    [XmlArrayItem("location")] 
    public Location[] Locations { get; set; } 
} 

public class Location 
{ 
    [XmlAttribute("country")] 
    public string Country { get; set; } 
    [XmlAttribute("city")] 
    public string City { get; set; } 
    [XmlAttribute("state")] 
    public string State { get; set; } 
} 
+0

Cela fonctionne réellement mais étant le programme un processus très délicat, j'ai besoin de faire plus de tests avant de le déployer. Je te le dirai bientôt. Merci en attendant! – GigiSan

+0

@GigiSan - Oui, bien sûr, testez-le minutieusement. Je ne suis pas sûr de l'exactitude complète de cette approche. –

+0

J'ai choisi d'aller avec la réponse de dbc parce que c'était proche de ce que j'avais déjà en tête et j'étais presque sûr du résultat. Étant donné le peu de temps dont je disposais, je n'ai pas pu tester rapidement votre code dans tous les cas. Mais de toute façon cela a fonctionné dans les premiers cas que j'ai essayés et je pense que les deux solutions sont tout aussi efficaces. Donc, merci beaucoup pour vos efforts. :) – GigiSan