XML-сериализация аннотаций - PullRequest
2 голосов
/ 06 апреля 2009

У меня есть ситуация, когда у меня есть XML-файл, который я не хочу изменять. Функция AddAnnotation в классе XElement предоставляет возможность добавлять данные только для памяти, которые не сериализуются и не являются частью XML.

Я хочу иметь возможность сохранить эти аннотации (например: в другой XML-файл), а затем десериализовать как XML, так и аннотации, чтобы получить тот же объект, который у меня был.

Я не хочу менять исходный xml, поэтому я использую аннотации.

Подводя итог, я хочу иметь возможность добавлять пользовательские данные в XML-файл. Эти данные не будут частью XML, когда я сериализую их, или они будут частью XML, но я смогу легко получить исходный XML.

У вас есть рекомендации, как я могу это сделать?

Редактировать: Должен ли я использовать инструкции по обработке XML? Инструкции по обработке предназначены для этого вида использования?

Ответы [ 2 ]

2 голосов
/ 06 апреля 2009

Мне кажется, что самый простой подход - использовать обычные узлы, но в другом пространстве имен xml, т.е.

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

(где myData - псевдоним xmlns для пространства имен-uri)

Во многих случаях читатели проверяют данные только в их пространстве имен (или пространстве имен по умолчанию / пусто) - значения в пользовательских пространствах имен обычно пропускаются.

Чтобы упаковать исходный xml, одним простым подходом было бы запустить его через xslt, который учитывает только пространство имен по умолчанию / оригинал.


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

Чтобы удалить такие данные из XElement, возможно:

    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();
        }
    }
1 голос
/ 29 июня 2009

В первоначальном вопросе использовалось слово «Serialize», но также упоминались XElement и аннотация. Для меня это две разные вещи.

Если вы действительно хотите использовать XmlSerializer:
То, что я хотел бы сделать, это использовать XmlAttributeOverrides, чтобы дифференцировать сериализацию. С XmlAttributeOverrides вы можете программно, во время выполнения, переопределить атрибуты сериализации xml, которые украшают ваши типы.

В вашем типе вы можете иметь поле / свойство, предназначенное для хранения аннотации / документации. Украсьте это с помощью XmlIgnore. Затем создайте один экземпляр XmlSerializer, который не принимает переопределений. Аннотация не будет сериализована или десериализована. Создайте другой экземпляр XmlSerializer для этого типа, используя объект XmlAttributeOverrides. Укажите переопределение для свойства XmlIgnore'd (используйте XmlElementAttribute), а также переопределения для любых атрибутов любого из других членов (используйте XmlIgnore = true).

Выполните сериализацию экземпляра дважды, по одному на каждый сериализатор.


Редактировать: вот код:

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");            
        // ---------------------------------------------------------------
    }
}

вывод с использованием встроенных атрибутов:

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

с использованием атрибутов переопределения:

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