2016-02-18 1 views
3

Je sais que la désérialisation des classes XML en C# est une chose courante et je l'ai déjà fait. Mais ive a reçu un fichier XML avec un format im ayant du mal à se désérialiser correctement. Quand je génère une classe C# à partir du XML. Je reçois un format que je ne veux pas. Fondamentalement toutes les propriétés obtiennent le type de colonne.Mappage du fichier XML dans une classe C#

Le format XML de base est comme ci-dessous. Le fait est que j'ai 250 propriétés et je ne veux vraiment pas cartographier manuellement chacun d'entre eux.

<item table="Order"> 
    <column columnName="Id"><![CDATA[2]]></column> 
    <column columnName="Price"><![CDATA[200]]></column> 
    <column columnName="Date"><![CDATA[25-01-2036 13:29:24:310]]> 

J'ai effectivement écrit manuellement une classe qui a les bonnes propriétés. Id, price, date et ainsi de suite ...

J'ai essayé avec l'ajout ElementName et AttributeName sans chance. Est-il possible pour moi de mapper ceci directement à une classe C# en utilisant XmlSerializer?

Il mappe parfaitement la classe autogenerated mais il se retrouve avec une liste de colonnes et columnName ..

Ne quelqu'un sait si je peux possible résoudre ce avec quelques notations xml sur mon C# classe pour obtenir la carte trop bien ?

-------------- -------------- Solution

Merci à @ Dan pour moi d'obtenir le terrain sur la bonne voie! Comme il le fait remarquer lui-même, il y avait un problème avec les attributs Nullable dans sa solution

J'ai donc chassé un autre et c'est ce que j'ai trouvé!

public void ReadXml(XmlReader reader) 
    { 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element && reader.Name == "column") 
      { 
       PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder)); 
       string propName = reader.GetAttribute("columnName"); 

       // Retrieve the property Descriptor 
       PropertyDescriptor prop = props[propName]; 

       if (prop != null) 
       { 
        reader.Read(); // move to CDATA (or text) node 

        // use DateTime.ParseExact instead for DateTime field 
        if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime)) 
        { 
         prop.SetValue(this, 
          DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture)); 
        } 
        else 
        { 
         prop.SetValue(this, 
          prop.Converter.ConvertFromInvariantString(reader.Value)); 
        } 
       } 
       else 
       { 
        throw new XmlException("Property not found: " + propName); 
       } 
      } 

     } 
    } 
+0

Montrez-nous ce que vous avez déjà essayé? – CodeNotFound

+0

Montrez-nous la classe que vous utilisez pour la désérialisation. – CodeNotFound

Répondre

0

Merci à @Dan Field pour m'avoir mis sur la bonne voie! Comme il le fait remarquer lui-même, il y avait un problème avec les attributs Nullable dans sa solution

J'ai donc chassé un autre et c'est ce que j'ai trouvé!

public void ReadXml(XmlReader reader) 
    { 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element && reader.Name == "column") 
      { 
       PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder)); 
       string propName = reader.GetAttribute("columnName"); 

       // Retrieve the property Descriptor 
       PropertyDescriptor prop = props[propName]; 

       if (prop != null) 
       { 
        reader.Read(); // move to CDATA (or text) node 

        // use DateTime.ParseExact instead for DateTime field 
        if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime)) 
        { 
         prop.SetValue(this, 
          DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture)); 
        } 
        else 
        { 
         prop.SetValue(this, 
          prop.Converter.ConvertFromInvariantString(reader.Value)); 
        } 
       } 
       else 
       { 
        throw new XmlException("Property not found: " + propName); 
       } 
      } 

     } 
    } 
1

Vous pourriez envisager d'utiliser XSLT pour transformer le XML à quelque chose XmlSerializer peut lire, mais si c'est juste, je pense que c'est un cas où vous voudrez implémenter IXmlSerializable sur une classe personnalisée. Voici quelque chose que j'ai fouetté ensemble, en utilisant la réflexion pour analyser le columnName dans une propriété de la classe Order.

Si vous devez sérialiser de nouveau à ce format, il devrait être assez trivial à faire.

[XmlRoot("item")] 
public class Order : IXmlSerializable 
{ 

    public int Id { get; set; } 
    public int Price { get; set; } 
    public DateTime Date { get; set; } 

    public System.Xml.Schema.XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     while (reader.Read()) 
     { 
      if (reader.NodeType == XmlNodeType.Element && reader.Name == "column") 
      { 
       string propName = reader.GetAttribute("columnName"); // get the property info... 
       PropertyInfo prop = typeof(Order).GetProperty(propName, 
         BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty); 
       if (prop != null && prop.CanWrite) 
       { 
        reader.Read(); // move to CDATA (or text) node 
        // can use Convert.ChangeType for most types 
        // use DateTime.ParseExact instead for DateTime field 
        if (prop.PropertyType != typeof(DateTime)) 
        { 
         prop.SetValue(this, 
          Convert.ChangeType(reader.Value, prop.PropertyType, CultureInfo.InvariantCulture)); 
        } 
        else 
        { 
         prop.SetValue(this, 
          DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.CurrentCulture)); 
        } 
       } 
       else 
       { 
        throw new XmlException("Property not found: " + propName); 
       } 
      }      

     } 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Vous aurez probablement à faire un peu plus de travail s'il y a d'autres types qui nécessitent des considérations d'analyse spéciales. Cela lèvera une exception si vous obtenez une valeur vide pour un nœud int cependant - pourrait fonctionner en utilisant simplement int? si c'est le cas. Il n'inclut pas non plus de génération d'auto-classe - j'écrirais un utilitaire pour le faire s'il y avait beaucoup de propriétés en jeu ou si elles changeaient fréquemment.

+0

Je vais certainement essayer quand j'aurai le temps! Je vais écrire à nouveau quand j'espère que ça marchera! ;) Si je comprends bien, je peux juste appeler mon désérialiseur comme normal et il utilisera ce readXml au lieu de celui par défaut? –

+0

Il aurait certainement besoin d'un peu plus de mise à jour pour une utilisation réelle - certains types ne seront pas convertis proprement en utilisant 'Convert.ChangeType', et il ne traitera pas avec des valeurs nulles si vous avez des champs nullables tels quels. –

+0

Et oui, il appellera la méthode 'ReadXml' pour effectuer le travail de désérialisation. –