Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

Мне кажется, что самым простым подходом было бы использовать обычные узлы, но в другом пространстве имен 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();
        }
    }

Другие советы

В исходном вопросе использовалось слово «Сериализация», но затем упоминались 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>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top