2015-08-07 1 views
3

Si j'ai un fichier xml qui ressembleles classes emboîtées désérialisés de XML plat

<Foo> 
    <Name>Some Data</Name> 
    <Bar_Data>Other Data</Bar_Data> 
    <Bar_MoreData>More Data</Bar_MoreData> 
</Foo> 

Et je veux tourner dans une classe C# qui ressemble à

public class Foo 
{ 
    public string Name {get; set; } 
    public Bar Bar { get; set; } 
} 

public class Bar 
{ 
    public string Data { get; set; } 
    public string MoreData { get; set; } 
} 

Est-il possible pour accomplir ceci avec juste des annotations simples de données (XmlRoot, XmlElement, etc.) ou est ma seule option pour implémenter IXmlSerializable?

EDIT: Notez que j'ai seulement besoin de désérialiser les données. Je reçois le XML d'une source tierce et je n'ai pas besoin de sérialiser Foo en XML (si cela le rend plus facile).

+0

1) Juste pour confirmer: vous utilisez 'XmlSerializer', n'est-ce pas? 2) Je ne pense pas que cela puisse être fait entièrement avec de simples annotations de données, mais il existe des méthodes plus simples que d'implémenter 'IXmlSerializable', ce qui est assez compliqué. Seraient-ce des réponses utiles? – dbc

+0

@bdc 1) J'utilise XmlSerializer en ce moment mais je n'y suis pas lié 3) Je suis ouvert à toute solution qui n'utilise pas de bibliothèque GPL (je ne peux utiliser aucune bibliothèque qui nécessiterait que je libère ma source code, mais LGPL ou tout autre OSS qui ne nécessite pas de libérer votre source est très bien) –

+0

@ScottChamberlain, l'une des options de ma réponse est-elle adaptée à votre problème ou vous avez d'autres contraintes que vous n'avez pas encore mentionnées? –

Répondre

4

Une option consiste à utiliser XmlAnyElementAttribute comme ci-dessous:

public class Foo 
{ 
    public string Name { get; set; } 
    public Bar Bar { get; set; } 

    [XmlAnyElementAttribute] 
    public XmlElement[] BarElements 
    { 
     get { return null; } 
     set 
     { 
      Bar = new Bar(); 
      var barType = Bar.GetType(); 
      foreach (var prop in value) 
       barType.GetProperty(prop.Name.Substring(4)).SetValue(Bar, prop.InnerText); 
     } 
    } 
} 

public class Bar 
{ 
    public string Data { get; set; } 
    public string MoreData { get; set; } 
} 

Lorsque XmlSerializer ne reconnaît pas un élément, il ajoute à la propriété de type XmlElement [] marqué par XmlAnyElementAttribute. C'est là que vous pouvez traiter les propriétés de la barre. J'ai utilisé la réflexion là-bas pour montrer l'idée.

Une autre option consiste à désérialiser deux fois et se connecter Bar avec Foo:

public class Foo 
{ 
    public string Name { get; set; } 
    public Bar Bar { get; set; } 
} 

[XmlRoot("Foo")] 
public class Bar 
{ 
    [XmlElement("Bar_Data")] 
    public string Data { get; set; } 
    [XmlElement("Bar_MoreData")] 
    public string MoreData { get; set; } 
} 

var foo = (Foo) new XmlSerializer(typeof (Foo)).Deserialize(...); 
var bar = (Bar) new XmlSerializer(typeof (Bar)).Deserialize(...); 
foo.Bar = bar 

Une autre option sans intrusion dans les classes étant désérialisée:

public class Foo 
{ 
    public string Name { get; set; } 
    public Bar Bar { get; set; } 
} 

public class Bar 
{ 
    public string Data { get; set; } 
    public string MoreData { get; set; } 
} 

var fooSerializer = new XmlSerializer(typeof (Foo)); 
fooSerializer.UnknownElement += (sender, e) => 
{ 
    var foo = (Foo) e.ObjectBeingDeserialized; 
    if(foo.Bar == null) 
     foo.Bar = new Bar(); 
    var propName = e.Element.Name.Substring(4); 
    typeof(Bar).GetProperty(propName).SetValue(foo.Bar, e.Element.InnerText); 
}; 

var fooInstance = fooSerializer.Deserialize(...); 

et si la double désérialisation ou de réflexion est la performance problématique sage, alors vous pouvez créer une classe proxy substitut:

[XmlRoot("Foo")] 
    public class FooSurrogate 
    { 
     public string Name { get; set; } 
     public string Bar_Data { get; set; } 
     public string Bar_MoreData { get; set; } 

     public Foo ToFoo() 
     { 
      return new Foo 
      { 
       Name = Name, 
       Bar = new Bar 
       { 
        Data = Bar_Data, 
        MoreData = Bar_MoreData 
       } 
      }; 
     } 
    } 

    var seializer = new XmlSerializer(typeof (FooSurrogate)); 
    var foo = ((FooSurrogate) seializer.Deserialize(...)).ToFoo();