c# - 使用 XmlSerializer 时如何向 XML 文件写入注释?

标签 c# .net xml xml-serialization xmlserializer

我有一个对象 Foo,我将其序列化为 XML 流。

public class Foo {
  // The application version, NOT the file version!
  public string Version {get;set;}
  public string Name {get;set;}
}

Foo foo = new Foo { Version = "1.0", Name = "Bar" };
XmlSerializer xmlSerializer = new XmlSerializer(foo.GetType());

这可以快速、轻松地完成当前所需的一切。

我遇到的问题是我需要维护一个单独的文档文件,其中包含一些小的注释。如上例所示,Name 很明显,但 Version 是应用程序版本,而不是本例中预期的数据文件版本。我还有很多类似的小事情想通过评论来澄清。

我知道如果我使用 WriteComment() 函数手动创建我的 XML 文件我可以做到这一点,但是是否有我可以实现的可能的属性或替代语法以便我可以继续使用序列化程序功能?

最佳答案

这可以通过使用返回 XmlComment 类型对象的属性来使用默认基础结构。并用 [XmlAnyElement("SomeUniquePropertyName")] 标记这些属性.

即如果像这样向 Foo 添加属性:

public class Foo
{
    [XmlAnyElement("VersionComment")]
    public XmlComment VersionComment { get { return new XmlDocument().CreateComment("The application version, NOT the file version!"); } set { } }

    public string Version { get; set; }
    public string Name { get; set; }
}

将生成以下 XML:

<Foo>
  <!--The application version, NOT the file version!-->
  <Version>1.0</Version>
  <Name>Bar</Name>
</Foo>

但是,问题的要求不止于此,即某种在文档系统中查找评论的方法。以下代码通过使用扩展方法根据反射(reflect)的评论属性名称查找文档来实现此目的:

public class Foo
{
    [XmlAnyElement("VersionXmlComment")]
    public XmlComment VersionXmlComment { get { return GetType().GetXmlComment(); } set { } }

    [XmlComment("The application version, NOT the file version!")]
    public string Version { get; set; }

    [XmlAnyElement("NameXmlComment")]
    public XmlComment NameXmlComment { get { return GetType().GetXmlComment(); } set { } }

    [XmlComment("The application name, NOT the file name!")]
    public string Name { get; set; }
}

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class XmlCommentAttribute : Attribute
{
    public XmlCommentAttribute(string value)
    {
        this.Value = value;
    }

    public string Value { get; set; }
}

public static class XmlCommentExtensions
{
    const string XmlCommentPropertyPostfix = "XmlComment";

    static XmlCommentAttribute GetXmlCommentAttribute(this Type type, string memberName)
    {
        var member = type.GetProperty(memberName);
        if (member == null)
            return null;
        var attr = member.GetCustomAttribute<XmlCommentAttribute>();
        return attr;
    }

    public static XmlComment GetXmlComment(this Type type, [CallerMemberName] string memberName = "")
    {
        var attr = GetXmlCommentAttribute(type, memberName);
        if (attr == null)
        {
            if (memberName.EndsWith(XmlCommentPropertyPostfix))
                attr = GetXmlCommentAttribute(type, memberName.Substring(0, memberName.Length - XmlCommentPropertyPostfix.Length));
        }
        if (attr == null || string.IsNullOrEmpty(attr.Value))
            return null;
        return new XmlDocument().CreateComment(attr.Value);
    }
}

为其生成以下 XML:

<Foo>
  <!--The application version, NOT the file version!-->
  <Version>1.0</Version>
  <!--The application name, NOT the file name!-->
  <Name>Bar</Name>
</Foo>

注意事项:

  • 扩展方法 XmlCommentExtensions.GetXmlCommentAttribute(this Type type, string memberName) 假定评论属性将被命名为 xxxXmlComment 其中 xxx 是“真正的”属性。如果是这样,它可以通过将传入的 memberName 属性标记为 CallerMemberNameAttribute 来自动确定真实的属性名称。 .这可以通过传入真实姓名手动覆盖。

  • 一旦类型和成员名称已知,扩展方法就会通过搜索应用于属性的 [XmlComment] 特性来查找相关注释。这可以替换为对单独文档文件的缓存查找。

  • 虽然仍然有必要为每个可能被注释的属性添加xxxXmlComment 属性,但这可能比implementing IXmlSerializable directly 更轻松。这非常棘手,可能导致反序列化错误,并且可能需要复杂子属性的嵌套序列化。

  • 要确保每个注释都在其关联元素之前,请参阅 Controlling order of serialization in C# .

  • 要使 XmlSerializer 序列化属性,它必须同时具有 getter 和 setter。因此,我给了评论属性 setter 什么也不做。

工作 .Net fiddle .

关于c# - 使用 XmlSerializer 时如何向 XML 文件写入注释?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7385921/

相关文章:

c# - 如何将 'out' 参数传递给 lambda 表达式

c# - 如何使用 JSONConvert 将变量 Result 转换为对象?

java - Java中XML节点的计数

c# - 打开网络浏览器,自动完成表单组件并提交

c# - 如何从 UWP 获取 Windows 的用户友好版本名称,如 'Windows 10 Pro'

c# - 自定义通用集合 - 重构它的最佳方法是什么?

.net - 通过 hWnd 获取 WPF 窗口

c# - 如何将参数传递给 TeamCity Test Runner?

php - 每周将 XML 文件保存到数据库

ios - NSXMLParser parse() 只工作一次