Pergunta

Eu tenho uma situação onde eu tenho um arquivo xml que eu não pretende modificar. A função AddAnnotation na classe XElement fornece uma opção para adicionar dados somente de memória que não é serializada e não fazem parte do XML.

Eu quero ser capaz de salvar essas anotações (por exemplo: para outro arquivo xml) e, em seguida, para desserializar tanto o xml e as anotações, a fim de obter o mesmo objeto que eu tinha.

Eu não quero mudar o xml original e essa é a razão que eu uso anotações.

Para resumir, eu quero ser capaz de adicionar dados personalizados para um arquivo xml. Estes dados não será uma parte do xml quando eu serializá-lo ou ele vai ser uma parte do xml, mas eu seria capaz de recuperar o XML original facilmente.

Você tem alguma recomendação como posso fazer uma coisa dessas?

Editar: Devo instruções uso de processamento de XML? Estão processando instruções destinado a este tipo de uso?

Foi útil?

Solução

It sounds to me like the simplest approach would be to use regular nodes, but in a different xml namespace - i.e.

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

(where myData is an xmlns alias for the namespace-uri)

In many cases, readers are only checking for data in their namespace (or the default/blank namespace) - values in custom namespaces are generally skipped.

To get pack the original xml, one simple approach would be to run it through an xslt that only respects the default/original namespace.


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

To remove such data from an XElement, perhaps:

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

Outras dicas

The original question used the word "Serialize" but then also mentioned XElement and annotation. To me these are two different things.

If you really want to use the XmlSerializer:
What I would do is use XmlAttributeOverrides, to differentiate the serialization. With the XmlAttributeOverrides you can programmatically, at runtime, override the xml serialization attributes that decorate your types.

Within your type, you can have a field/property that is intended to hold the annotation/documentation. Decorate that with XmlIgnore. Then, create one instance of the XmlSerializer that accepts no overrides. The annotation will not be serialized or de-serialized. Create another instance of the XmlSerializer for that type, using an XmlAttributeOverrides object. Specify an override for the XmlIgnore'd property (use XmlElementAttribute), as well as overrides for any attributes on any of the other members (use XmlIgnore=true).

Serialize the instance twice, one with each serializer.


Edit: here's the 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");            
        // ---------------------------------------------------------------
    }
}

output, using the in-line attributes:

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

output, using the override attributes:

<DTO-Annotations xmlns="urn:www.example.org">
  <note>This will bbe serialized separately</note>
</DTO-Annotations>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top