2009-04-06 7 views
2

J'ai une situation dans laquelle j'ai un fichier XML que je ne veux pas modifier. La fonction AddAnnotation de la classe XElement offre une option permettant d'ajouter des données en mémoire uniquement qui ne sont pas sérialisées et ne font pas partie du code XML. Je souhaite pouvoir enregistrer ces annotations (par exemple: dans un autre fichier xml), puis désérialiser le xml et les annotations afin d'obtenir le même objet que celui que j'avais.Annotations XML sérialiser

Je ne veux pas modifier le fichier XML d'origine et c'est la raison pour laquelle j'utilise des annotations.

En résumé, je veux être en mesure d'ajouter des données personnalisées à un fichier XML. Ces données ne feront pas partie du xml quand je le sérialiser ou il fera partie du xml mais je serais capable de récupérer facilement le xml d'origine.

Avez-vous des recommandations sur comment je peux faire une telle chose?

Modifier: Dois-je utiliser des instructions de traitement XML? Les instructions de traitement sont-elles destinées à ce type d'utilisation?

Répondre

2

Il me semble que la simple approche serait d'utiliser des nœuds réguliers, mais dans un espace de noms XML différent - à savoir

<foo standardAttrubute="abc" myData:customAttribute="def"> 
    <standardElement>ghi</standardElement > 
    <myData:customElement>jkl</myData:customElement> 
</foo> 

(où myData est un alias xmlns pour l'namespace-uri)

Dans de nombreux cas, les lecteurs vérifient uniquement les données dans leur espace de noms (ou l'espace de noms vide/vide) - les valeurs dans les espaces de noms personnalisés sont généralement ignorées. Pour obtenir le fichier XML d'origine, une approche simple serait de l'exécuter à travers un xslt qui ne respecte que l'espace de noms par défaut/original.


XNamespace myData = XNamespace.Get("http://mycustomdata/"); 
XElement el = new XElement("foo", 
    new XAttribute(XNamespace.Xmlns + "myData", myData.NamespaceName), 
    new XAttribute("standardAttribute", "abc"), 
    new XAttribute(myData + "customAttribute", "def"), 
    new XElement("standardElement", "ghi"), 
    new XElement(myData + "customAttribute", "jkl")); 
string s = el.ToString(); 

Pour supprimer ces données d'un XElement, peut-être:

static void Strip(XElement el, XNamespace ns) { 
     List<XElement> remove = new List<XElement>(); 
     foreach (XElement child in el.Elements()) { 
      if (child.Name.Namespace == ns) { 
       remove.Add(child); 
      } else { 
       Strip(child, ns); 
      } 
     } 
     remove.ForEach(child => child.Remove()); 

     foreach (XAttribute child in 
      (from a in el.Attributes() 
      where a.Name.Namespace == ns 
      select a).ToList()) { 
      child.Remove(); 
     } 
    } 
+0

Sounds intéressant ... Pouvez-vous donner un exemple (deux exemples en fait) comment Je peux charger le XML en utilisant XElement, avec et sans l'attribut personnalisé? –

+0

Création ajoutée - c'est ce que vous vouliez dire? Ou autre chose? –

+0

Non. Je sais comment créer le fichier XML. Je ne sais pas comment le charger sans les données personnalisées. tnx! –

1

La question originale a utilisé le mot "sérialisation" mais aussi mentionné XElement et l'annotation. Pour moi, ce sont deux choses différentes.

Si vous voulez vraiment utiliser XmlSerializer:
Ce que je voudrais faire est l'utilisation XmlAttributeOverrides, pour différencier la sérialisation. Avec XmlAttributeOverrides, vous pouvez, au moment de l'exécution, remplacer par programme les attributs de sérialisation xml qui décorent vos types.

Dans votre type, vous pouvez avoir un champ/une propriété destiné à contenir l'annotation/la documentation. Décorez cela avec XmlIgnore. Ensuite, créez une instance de XmlSerializer qui n'accepte aucune substitution. L'annotation ne sera pas sérialisée ou dé-sérialisée. Créez une autre instance de XmlSerializer pour ce type en utilisant un objet XmlAttributeOverrides. Spécifiez un remplacement pour la propriété XmlIgnore'd (utilisez XmlElementAttribute), ainsi que des remplacements pour les attributs de tous les autres membres (utilisez XmlIgnore = true).

Sérialiser l'instance deux fois, une avec chaque sérialiseur.


Edit: voici le code:

public class DTO 
{ 
    [XmlIgnore] 
    public string additionalInformation; 

    [XmlElement(Order=1)] 
    public DateTime stamp; 

    [XmlElement(Order=2)] 
    public string name; 

    [XmlElement(Order=3)] 
    public double value; 

    [XmlElement(Order=4)] 
    public int index; 
} 



public class OverridesDemo 
{ 
    public void Run() 
    { 
     DTO dto = new DTO 
      { 
       additionalInformation = "This will bbe serialized separately", 
       stamp = DateTime.UtcNow, 
       name = "Marley", 
       value = 72.34, 
       index = 7 
      }; 


     // --------------------------------------------------------------- 
     // 1. serialize normally 
     // this will allow us to omit the xmlns:xsi namespace 
     var ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 

     XmlSerializer s1 = new XmlSerializer(typeof(DTO)); 

     var builder = new System.Text.StringBuilder(); 
     var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent= true }; 

     Console.WriteLine("\nSerialize using the in-line attributes: "); 
     using (XmlWriter writer = XmlWriter.Create(builder, settings)) 
     { 
      s1.Serialize(writer, dto, ns); 
     } 
     Console.WriteLine("{0}",builder.ToString()); 
     Console.WriteLine("\n");    
     // --------------------------------------------------------------- 

     // --------------------------------------------------------------- 
     // 2. serialize with attribute overrides 
     // use a non-empty default namespace 
     ns = new XmlSerializerNamespaces(); 
     string myns = "urn:www.example.org"; 
     ns.Add("", myns); 

     XmlAttributeOverrides overrides = new XmlAttributeOverrides(); 

     XmlAttributes attrs = new XmlAttributes(); 
     // override the (implicit) XmlRoot attribute 
     XmlRootAttribute attr1 = new XmlRootAttribute 
      { 
       Namespace = myns, 
       ElementName = "DTO-Annotations", 
      }; 
     attrs.XmlRoot = attr1; 

     overrides.Add(typeof(DTO), attrs); 
     // "un-ignore" the first property 
     // define an XmlElement attribute, for a type of "String", with no namespace 
     var a2 = new XmlElementAttribute(typeof(String)) { ElementName="note", Namespace = myns }; 

     // add that XmlElement attribute to the 2nd bunch of attributes 
     attrs = new XmlAttributes(); 
     attrs.XmlElements.Add(a2); 
     attrs.XmlIgnore = false; 

     // add that bunch of attributes to the container for the type, and 
     // specifically apply that bunch to the "additionalInformation" property 
     // on the type. 
     overrides.Add(typeof(DTO), "additionalInformation", attrs); 

     // now, XmlIgnore all the other properties 
     attrs = new XmlAttributes(); 
     attrs.XmlIgnore = true;  
     overrides.Add(typeof(DTO), "stamp", attrs); 
     overrides.Add(typeof(DTO), "name", attrs); 
     overrides.Add(typeof(DTO), "value", attrs); 
     overrides.Add(typeof(DTO), "index", attrs); 

     // create a serializer using those xml attribute overrides 
     XmlSerializer s2 = new XmlSerializer(typeof(DTO), overrides); 

     Console.WriteLine("\nSerialize using the override attributes: "); 
     builder.Length = 0; 
     using (XmlWriter writer = XmlWriter.Create(builder, settings)) 
     { 
      s2.Serialize(writer, dto, ns); 
     } 
     Console.WriteLine("{0}",builder.ToString()); 
     Console.WriteLine("\n");    
     // --------------------------------------------------------------- 
    } 
} 

sortie, en utilisant les attributs en ligne:

<DTO> 
    <stamp>2009-06-30T02:17:35.918Z</stamp> 
    <name>Marley</name> 
    <value>72.34</value> 
    <index>7</index> 
</DTO> 

sortie, en utilisant les attributs de remplacement:

<DTO-Annotations xmlns="urn:www.example.org"> 
    <note>This will bbe serialized separately</note> 
</DTO-Annotations> 
+0

Je ne suis pas sûr d'avoir suivi. Pouvez-vous fournir un extrait? tnx ... –