2010-12-01 6 views
5

Comment puis-je accéder aux champs XmlAttributes d'un objet IXmlSerializable en utilisant XmlAttributesOverrides?Accès aux attributs XmlAttributesOverrides ajoutés dans les méthodes IXmlSerializable

échantillon objet IXmlSerializable:

public class Person : SomeBaseClass, IXmlSerializable 
{ 
    public string Name1; 

    public string Name2; 

    [XmlIgnore] 
    public string Name3; 

    public Person() 
    { 
    } 

    public Person(string first, string second, string third) 
    { 
     Name1 = first; 
     Name2 = second; 
     Name3 = third; 
    } 

    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     // .... 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     FieldInfo[] finfo = this.GetType().GetFields(); 

     foreach (FieldInfo finf in finfo) 
     { 
      FieldAttributes attr = finf.Attributes; 
      object[] atts = finf.GetCustomAttributes(true); 

      if (atts.Length == 0) 
      { 
       // handle field with no attributes ... should be just Name1 
       // but also get Name2 since XmlAttributOverrides' XmlIgnore is not 
       // included with GetCustomAttributes results. 
       writer.WriteElementString(finf.Name, (string)finf.GetValue(this)); 
      } 
      else 
      { 
       // handle field with attributes ... should be Name2 and Name3 
       // but only get Name3 via [XmlIgnore] compiler generated attribute 
      } 
     } 
    } 
} 

création Override typique:

 // parent app ... 

    public XmlSerializer CreateOverrider() 
    { 
     XmlAttributeOverrides xOver = new XmlAttributeOverrides(); 
     XmlAttributes attrs = new XmlAttributes(); 

     attrs.XmlIgnore = true; 
     xOver.Add(typeof(Person), "name2", attrs); 

     XmlSerializer xSer = new XmlSerializer(typeof(Person), xOver); 
     return xSer; 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     // Custom XmlSerialize 
     Person pson = new Person("First", "Second", "Third"); 

     XmlSerializer serializer = CreateOverrider(); 
     TextWriter writer = new StreamWriter("PersonOverride.xml"); 

     serializer.Serialize(writer, pson); 
     writer.Close(); 
    } 

    // etc ... 

Créé Sortie:

<?xml version="1.0" encoding="utf-8"?><Person><Name1>First</Name1><Name2>Second</Name2></Person> 

Je dois utiliser IXmlSerializable pour surmonter les problèmes d'héritage de 'SomeBaseClass'. Le problème est que GetCustomArributes ne retourne aucun des attributs ajoutés à la collection XmlAttributeOverrides - ou je le fais mal!?

Il est également probable que GetCustomAttributes n'est pas SUPPOSÉ pour renvoyer de tels attributs ajoutés, ou que je ne suis pas censé utiliser XmlAttributeOverrides dans une classe IXmlSerializable.

Alors ... des idées ou des alternatives. Merci d'avoir pris le temps.

+0

est-il une raison particulière, pourquoi vous ne pouvez pas les injecter directement à la classe personne? Par exemple rendre la méthode SetOverrides (XmlAttributeOverrides overrides) sur la classe Person et l'appeler juste avant qu'il ne soit sérialisé? –

+0

@OndrejSvejdar Cela fonctionnera pour la sérialisation mais PAS pour la désérialisation. –

Répondre

0

Il n'y a aucun moyen de faire cela.

La raison est que XmlSerializer génère des classes de sérialiseurs lorsque des objets donnés ne sont pas IXmlSerializable. Ces attributs XML Overrides seront utilisés pour compiler ces classes différemment. Remplacements XML Les attributs ne s'appliquent pas à l'exécution pendant la sérialisation ou la désérialisation; c'est pourquoi ils ne sont pas accessibles.

Les classes qui héritent IXmlSerializable ne génèrent pas de classe de sérialiseur. Si vous souhaitez utiliser les attributs de remplacement XML, vous ne devrez pas surcharger le compilateur de classe de sérialiseur. Utilisez cette mise en œuvre de Person au lieu et le laisser générer la classe sérialiseur pour vous en fonction des remplacements donnés (également exécuter plusieurs de fois plus rapide):

public class Person : SomeBaseClass 
{ 
    public string Name1; 

    public string Name2; 

    [XmlIgnore] 
    public string Name3; 

    public Person() 
    { 
    } 

    public Person(string first, string second, string third) 
    { 
     Name1 = first; 
     Name2 = second; 
     Name3 = third; 
    } 
} 

Vous avez bien sûr sont également invités à écrire votre propre compilateur classe sérialiseur , mais c'est un peu plus complexe que ce qui peut convenir ici. Mais la mise en œuvre devrait ressembler à ceci:

public static Type GeneratePersonSerializer(XmlAttributeOverrides overrides) { 
    //here compile a class to generate a Type inheriting from IXmlSerializable 
    //the serializer logic in this class should be generated by taking into 
    //account the given XmlAttributeOverrides 
    //the type returned should be the Type passed into new XmlSerializer(Type type) 
} 
0

peut offrir de créer votre propre implémentation de XmlWriter avec la liste des FieldInfo où vous stockerez les références aux champs à sérialiser. Puis passez-le à l'instance du type de cible (Personne dans l'exemple) et sérialisez-les uniquement. Dans la méthode principale, vous pouvez voir 2 exemples de sérialisation: avec Name1 et sans Name1. Et devrait également noter que la performance de réflexion est lente, donc peut-être simplement créer un champ booléen (bool DoNotSerializeName1) et si ce sera vrai, alors ignorer la sérialisation Name1. J'espère que cela sera utile pour quelqu'un ...

exemple type de personne et sérialisation:

using System; 
using System.IO; 
using System.Linq; 
using System.Reflection; 
using System.Text; 
using System.Xml; 
using System.Xml.Schema; 
using System.Xml.Serialization; 

namespace XmlCustomSerializer 
{ 
    public class Person : IXmlSerializable 
    { 
     public string Name1; 

     public string Name2; 

     [XmlIgnore] 
     public string Name3; 

     public Person() 
     { 
     } 

     public Person(string first, string second, string third) 
     { 
      Name1 = first; 
      Name2 = second; 
      Name3 = third; 
     } 

     public XmlSchema GetSchema() 
     { 
      return null; 
     } 

     public void ReadXml(XmlReader reader) 
     { 
      // .... 
     } 

     private static FieldInfo[] _cachedFields = null; 
     public void WriteXml(XmlWriter writer) 
     { 
      var customWriter = writer as XmlCustomWriter; 
      if (customWriter == null) 
       throw new ArgumentException("writer"); 

      if (_cachedFields == null) 
      { 
       _cachedFields = typeof(Person).GetFields(); 
      } 

      foreach (FieldInfo finf in customWriter.FieldsToSerialize) 
      { 
       if (_cachedFields.Contains(finf)) 
       { 
        writer.WriteElementString(finf.Name, (string)finf.GetValue(this)); 
       } 
      } 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var person = new Person 
      { 
       Name1 = "Some", 
       Name2 = "Person", 
       Name3 = "Name" 
      }; 

      var settings = new XmlWriterSettings { Indent = true, Encoding = Encoding.ASCII }; 
      var allFields = typeof(Person).GetFields(); 

      XmlSerializer xSer = new XmlSerializer(typeof(Person)); 

      using (var stream = new MemoryStream()) 
      { 
       var xmlCustomWriter = new XmlCustomWriter(
        XmlWriter.Create(new StreamWriter(stream), settings)); 

       //serialize all fields 
       xmlCustomWriter.FieldsToSerialize = allFields; 

       xSer.Serialize(xmlCustomWriter, person); 

       stream.Seek(0, SeekOrigin.Begin); 
       Console.WriteLine(new StreamReader(stream).ReadToEnd()); 
      } 

      using (var stream = new MemoryStream()) 
      { 
       var xmlCustomWriter = new XmlCustomWriter(
        XmlWriter.Create(new StreamWriter(stream), settings)); 

       //serialize 2 fields 
       xmlCustomWriter.FieldsToSerialize = allFields.Skip(1); 

       xSer.Serialize(xmlCustomWriter, person); 

       stream.Seek(0, SeekOrigin.Begin); 
       Console.WriteLine(new StreamReader(stream).ReadToEnd()); 
      } 

      Console.Read(); 
     } 
    } 
} 

mise en œuvre de XmlCustomWriter:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using System.Xml; 

namespace XmlCustomSerializer 
{ 
    public class XmlCustomWriter : XmlWriter 
    { 
     private readonly XmlWriter _innerWriter; 

     public XmlCustomWriter(XmlWriter innerWriter) 
     { 
      if (innerWriter == null) 
       throw new ArgumentNullException("innerWriter"); 
      _innerWriter = innerWriter; 
      FieldsToSerialize = Enumerable.Empty<FieldInfo>(); 
     } 

     public IEnumerable<FieldInfo> FieldsToSerialize { get; set; } 

     #region Implement XmlWriter 
     public override void Flush() 
     { 
      _innerWriter.Flush(); 
     } 

     public override string LookupPrefix(string ns) 
     { 
      return _innerWriter.LookupPrefix(ns); 
     } 

     public override void WriteBase64(byte[] buffer, int index, int count) 
     { 
      _innerWriter.WriteBase64(buffer, index, count); 
     } 

     public override void WriteCData(string text) 
     { 
      _innerWriter.WriteCData(text); 
     } 

     public override void WriteCharEntity(char ch) 
     { 
      _innerWriter.WriteCharEntity(ch); 
     } 

     public override void WriteChars(char[] buffer, int index, int count) 
     { 
      _innerWriter.WriteChars(buffer, index, count); 
     } 

     public override void WriteComment(string text) 
     { 
      _innerWriter.WriteComment(text); 
     } 

     public override void WriteDocType(string name, string pubid, string sysid, string subset) 
     { 
      _innerWriter.WriteDocType(name, pubid, sysid, subset); 
     } 

     public override void WriteEndAttribute() 
     { 
      _innerWriter.WriteEndAttribute(); 
     } 

     public override void WriteEndDocument() 
     { 
      _innerWriter.WriteEndDocument(); 
     } 

     public override void WriteEndElement() 
     { 
      _innerWriter.WriteEndElement(); 
     } 

     public override void WriteEntityRef(string name) 
     { 
      _innerWriter.WriteEntityRef(name); 
     } 

     public override void WriteFullEndElement() 
     { 
      _innerWriter.WriteFullEndElement(); 
     } 

     public override void WriteProcessingInstruction(string name, string text) 
     { 
      _innerWriter.WriteProcessingInstruction(name, text); 
     } 

     public override void WriteRaw(string data) 
     { 
      _innerWriter.WriteRaw(data); 
     } 

     public override void WriteRaw(char[] buffer, int index, int count) 
     { 
      _innerWriter.WriteRaw(buffer, index, count); 
     } 

     public override void WriteStartAttribute(string prefix, string localName, string ns) 
     { 
      _innerWriter.WriteStartAttribute(prefix, localName, ns); 
     } 

     public override void WriteStartDocument(bool standalone) 
     { 
      _innerWriter.WriteStartDocument(standalone); 
     } 

     public override void WriteStartDocument() 
     { 
      _innerWriter.WriteStartDocument(); 
     } 

     public override void WriteStartElement(string prefix, string localName, string ns) 
     { 
      _innerWriter.WriteStartElement(prefix, localName, ns); 
     } 

     public override WriteState WriteState 
     { 
      get { return _innerWriter.WriteState; } 
     } 

     public override void WriteString(string text) 
     { 
      _innerWriter.WriteString(text); 
     } 

     public override void WriteSurrogateCharEntity(char lowChar, char highChar) 
     { 
      _innerWriter.WriteSurrogateCharEntity(lowChar, highChar); 
     } 

     public override void WriteWhitespace(string ws) 
     { 
      _innerWriter.WriteWhitespace(ws); 
     } 
     #endregion Implement XmlWriter 
    } 
}