Доступ к добавленным атрибутам XmlAttributesOverrides в методах IXmlSerializable - PullRequest
5 голосов
/ 02 декабря 2010

Как получить доступ к XmlAttributes, примененному к полям в IXmlSerializable объекте, используя XmlAttributesOverrides?

Пример объекта 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
            }
        }
    }
}

Типичное создание переопределения:

        // 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 ...

Созданный вывод:

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

Мне нужно использовать IXmlSerializable, чтобы преодолеть проблемы наследования от SomeBaseClass.

Проблема в том, что GetCustomArributes не возвращает никаких атрибутов, добавленных в коллекцию XmlAttributeOverrides - или я делаю это неправильно!?

Также вероятно, что GetCustomAttributes НЕ ДОЛЖЕН возвращать такие добавленные атрибуты или что я не должен использовать XmlAttributeOverrides в IXmlSerializable классе.

Итак ... любые идеи или альтернативы. Спасибо, что нашли время.

Ответы [ 2 ]

0 голосов
/ 13 мая 2017

Может предложить создать собственную реализацию XmlWriter со списком FieldInfo, где вы будете хранить ссылки на поля для сериализации. Затем передайте его экземпляру целевого типа (например, Person ) и сериализуйте только их. В основном методе вы можете увидеть 2 примера сериализации: с Name1 и без Name1. Также следует заметить, что производительность отражения медленная, поэтому можно просто создать поле bool ( bool DoNotSerializeName1 ) и, если оно будет истинным, игнорировать сериализацию Name1. Надеюсь, это кому-нибудь пригодится ...

Тип персоны и пример сериализации:

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();
        }
    }
}

Реализация 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
    }
}
0 голосов
/ 26 августа 2013

Нет способа сделать это.

Причина в том, что XmlSerializer будет генерировать классы сериализатора, если заданы объекты, которые не являются IXmlSerializable. Эти атрибуты XML Overrides будут использоваться для компиляции этих классов по-разному. Атрибуты переопределения XML не применяются во время выполнения во время сериализации или десериализации ; вот почему они не доступны.

Классы, которые наследуют IXmlSerializable, не генерируют класс сериализатора. Если вы хотите использовать атрибуты переопределения XML, вам не нужно переопределять компилятор класса сериализатора. Вместо этого используйте эту реализацию Person и дайте ей сгенерировать для вас класс сериализатора на основе заданных переопределений (также будет выполняться во много раз быстрее):

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;
    }
}

Конечно, вы также можете написать свой собственный компилятор класса сериализатора, но это немного сложнее, чем то, что здесь уместно. Но реализация должна выглядеть примерно так:

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)
}
...