2

我有一个不想修改的 xml 文件的情况。XElement 类中的 AddAnnotation 函数提供了一个选项来添加未序列化且不属于 XML 的仅内存数据。

我希望能够保存这些注释(例如:到另一个 xml 文件),然后对 xml 和注释进行反序列化,以便获得与我相同的对象。

我不想更改原始 xml,这就是我使用注释的原因。

总而言之,我希望能够将自定义数据添加到 xml 文件中。当我序列化它时,这些数据不会成为 xml 的一部分,或者它将成为 xml 的一部分,但我可以轻松地检索原始 xml。

你有什么建议我可以做这样的事情吗?

编辑:我应该使用 xml 处理指令吗?处理指令是否适用于这种用途?

4

2 回答 2

2

在我看来,最简单的方法是使用常规节点,但在不同的 xml 命名空间中 - 即

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

(其中myDataxmlnsnamespace-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();
        }
    }
于 2009-04-06T06:50:23.403 回答
1

最初的问题使用了“序列化”这个词,但后来也提到了 XElement 和注释。对我来说,这是两件不同的事情。

如果您真的想使用 XmlSerializer:
我会使用 XmlAttributeOverrides 来区分序列化。使用 XmlAttributeOverrides,您可以在运行时以编程方式覆盖装饰您的类型的 xml 序列化属性。

在您的类型中,您可以拥有一个用于保存注释/文档的字段/属性。用 XmlIgnore 装饰它。然后,创建一个不接受任何覆盖的 XmlSerializer 实例。注释不会被序列化或反序列化。使用 XmlAttributeOverrides 对象为该类型创建另一个 XmlSerializer 实例。为 XmlIgnore 的属性指定一个覆盖(使用 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>
于 2009-06-29T19:23:42.203 回答