【问题标题】:DataContract + IxmlSerializableDataContract + IxmlSerializable
【发布时间】:2016-05-26 07:25:00
【问题描述】:

我们在 Java 服务器上创建了一个带有“自定义对象和属性”的项目,并且在 C# 客户端上需要这些数据。

例如自定义对象“A”具有属性“B”、“C”。 “B”和“C”都由客户在运行时描述。服务器以 XML 格式将其发送给我们,例如:

<A>
    <B> B Data </B>
    <C> C Data </C>
</A>

我们创建了一个实现 IXmlSerializable 的类,它读取/写入 xml 以供服务器将自定义属性填充到字典中

public class CustomObject : IXmlSerializable
{
    private Dictionary<String, String> attributes;

    public void ReadXml(XmlReader reader)
    {
        attributes = XDocument.Parse(reader.ReadOuterXml()).Root.Elements()
            .ToDictionary(xElm => xElm.Name.LocalName, xElm => xElm.Value)
    }

    // More Serialization logic for IXmlSerializable is here
}

项目很慢,我们想使用更快的 DataContract 序列化。 我们通过在我们的属性(如“B”、“C”)上明确指定硬编码 [DataContract] 来对样本进行测试。然而,在我们的用例中,属性在编译时是未知的。我们可以向服务器查询类型“A”的属性列表。

我们如何将 DataContract 用于运行时定义的属性

【问题讨论】:

  • 您可以尝试在您的CustomObject 上实现IExtensibleDataObject,然后使用ExtensionDataObject not marked as serializable 中的技巧提取XML。但是,您是否进行了分析以确定真正的问题出在哪里?
  • 你为什么要XDocument.Parse(reader.ReadOuterXml())?这有效地解析了 XML 两次。如果您遇到性能问题,可以将其替换为 ReadSubtree()
  • 感谢@dbc 的反馈,XDocument 似乎为每个对象花费了大约 16 毫秒,并且还增加了我们的内存签名。这就是我们需要 DataContract 的原因。我会检查你分享的链接。
  • @dbc 函数中有更多逻辑。我只是为了演示而减少了它。 ReadSubtree() 会提供 XElement 吗? XElement 是我们项目一部分的进一步逻辑所必需的
  • 其实XNode.ReadFrom()会更好。答案已更新。

标签: c# serialization linq-to-xml datacontract ixmlserializable


【解决方案1】:

DataContractSerializer 不支持允许任意未知元素的显式数据协定。 XmlSerializer 通过[XmlAnyElementAttribute] 支持这一点,但正如答案Using [XmlAnyElement] 中所述,数据合约没有相同的功能。

你的类可以实现IExtensibleDataObject。它类似于[XmlAnyElement],适用于forward-compatible data contracts。不幸的是,在这种情况下,未知元素存储在不透明的ExtensionDataObject 中,没有明显的方法可以访问这些值。虽然可以从这样的对象中提取 XML(请参阅 here),但它并不明显,并且不太可能比您当前的代码性能更高,因为它需要在包装类中重新序列化 ExtensionDataObject,然后解析结果。

关于性能的注意事项 - 当您执行 XDocument.Parse(reader.ReadOuterXml()) 时,reference source 表明您正在有效地解析 XML,然后通过 XmlWriter 将其流式传输到 StringWriter,然后再次解析生成的字符串。您可以通过在传入阅读器上调用 XNode.ReadFrom() 来仅解析一次 XML,而不是这样做,如下所示:

public class CustomObject : IXmlSerializable
{
    private readonly Dictionary<String, String> attributes = new Dictionary<string, string>();

    public IDictionary<string, string> Attributes { get { return attributes; } }

    #region IXmlSerializable Members

    System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
    {
        return null;
    }

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
        var element = XElement.ReadFrom(reader) as XElement;
        if (element != null)
        {
            foreach (var item in element.Elements())
                attributes.Add(item.Name.LocalName, (string)item);
        }
    }

    void IXmlSerializable.WriteXml(XmlWriter writer)
    {
        // Do NOT write the wrapper element when writing.
        foreach (var pair in attributes)
        {
            writer.WriteElementString(pair.Key, pair.Value);
        }
    }

    #endregion
}

这应该比您当前的课程更高效。例如,Web API performance issues with large dynamic XML 报告的类似优化改进为 40%。

更新

为了实现IXmlSerializable 的最佳性能,您需要使用定制代码直接从XmlReader 读取内容。例如,以下将元素名称和值读入attributes 字典:

    void IXmlSerializable.ReadXml(XmlReader reader)
    {
        if (reader.IsEmptyElement)
        {
            reader.Read();
            return;
        }
        reader.Read();
        while (reader.NodeType != XmlNodeType.EndElement)
        {
            switch (reader.NodeType)
            {
                case XmlNodeType.Element:
                    var key = reader.Name;
                    var value = reader.ReadElementContentAsString();
                    attributes.Add(key, value);
                    break;

                default:
                    // Comment, for instance.
                    reader.Read();
                    break;
            }
        }
        // Consume the EndElement
        reader.Read();
    }

请参阅Proper way to implement IXmlSerializable?,了解有关正确手动读取元素层次结构的一些一般准则。

【讨论】:

  • 试过这个,我发现你的解决方案是一个有效的性能问题,但它并没有增加显着的变化,所以我们仍然需要一个更好的解决方案。 ExtensionDataObject 也无济于事,因为我们确实需要访问自定义属性中的数据。
  • @KitiAzura - 解析 XML 的最高效方法是手动进行。来自Chapter 9 — Improving XML Performance如果您想读取文档一次,请使用 [XmlReader]。这提供了对 XML 数据的只进、只读和非缓存访问。此模型提供优化的性能和内存节省。 另请参阅Effective Xml Part 1: Choose the right API
  • 我认为您的上述评论看起来是正确的答案。在我们遇到 DataContract 之前,至少这是我想做的。
  • @KitiAzura - 我添加了ReadXml() 的定制版本,它直接与XmlReader 一起使用。除此之外,您可能需要分析和分享有关您测量的性能瓶颈的更多详细信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-30
  • 1970-01-01
  • 1970-01-01
  • 2011-02-09
  • 2022-10-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多