【问题标题】:Deserialize XML Fragment with Namespace using C#使用 C# 使用命名空间反序列化 XML 片段
【发布时间】:2015-06-28 19:17:25
【问题描述】:

我在反序列化以下 XML 片段(来自 OneNote)时遇到问题:

<one:OE creationTime="2015-03-21T18:32:38.000Z" lastModifiedTime="2015-03-21T18:32:38.000Z" objectID="{649CA68C-C596-4F89-9885-1553A953529E}{30}{B0}" alignment="left" quickStyleIndex="1" selected="partial">
    <one:List>
        <one:Bullet bullet="2" fontSize="11.0" />
    </one:List>
    <one:T><![CDATA[Bullet point one]]></one:T>
</one:OE>

以下代码用于反序列化上述片段。 OE 类具有以下属性:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34230")]
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote")]
[System.Xml.Serialization.XmlRootAttribute("OE", Namespace = "http://schemas.microsoft.com/office/onenote/2013/onenote", IsNullable = true)]
public partial class OE : EntityBase<OE>
{
    ...
}

反序列化片段的实际方法是在基类EntityBase中:

public static T Deserialize(string xml)
{
    System.IO.StringReader stringReader = null;
    try
    {
        stringReader = new System.IO.StringReader(xml);
        return ((T)(Serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
    }
    finally
    {
        if ((stringReader != null))
        {
            stringReader.Dispose();
        }
    }
}

反序列化方法调用如下:

var element = OE.Deserialize(xmlString);

其中变量xmlString 是上面给出的XML 片段。在调用Deserialize 方法时,我收到以下错误:

There is an error in XML document (1,2). ---> System.Xml.XmlException: 'one' is an undeclared prefix. Line 1, position 2.

我花了一些时间查看在 OE 类中声明命名空间的属性,但一切似乎都是正确的。谁能指出我犯的错误?

【问题讨论】:

  • 错误告诉您确切的问题:命名空间one 未定义。 IE。不存在启动 one 的 xmlns 属性。
  • 由于缺少命名空间声明,我认为这只是从较大的 XML 文件中截取的?是否以编程方式完成?如果是这样,请显示此代码 - 它需要更改为可识别 XML。蛮力砍掉这个元素会导致无效的 XML。

标签: c# xml deserialization xml-deserialization


【解决方案1】:

matrixanomaly 给出的answer 是正确的,可惜给出的 OneNote 命名空间不正确。我正在使用 OneNote 2013 而不是 2010。我用来反序列化与我的问题中给出的相同 XML 片段的实际代码如下:

public static OE DeserializeFragment(string xmlFragment)
{
    var serializer = new System.Xml.Serialization.XmlSerializer(typeof(OE));
    System.IO.StringReader stringReader = null;
    try
    {
        stringReader = new System.IO.StringReader(xmlFragment);

        NameTable nt = new NameTable();
        XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
        nsManager.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2013/onenote");
        XmlParserContext context = new XmlParserContext(null, nsManager, null, XmlSpace.None);
        XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
        xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;

        return ((OE)(serializer.Deserialize(System.Xml.XmlReader.Create(stringReader, xmlReaderSettings, context))));
    }
    finally
    {
        if ((stringReader != null))
        {
            stringReader.Dispose();
        }
    }
}

【讨论】:

  • 奇怪,我没有收到您提到的通知,只是在我重新访问我的一些答案时才看到它! +1 更新!
【解决方案2】:

我认为您需要 one 的原始命名空间声明。这是因为one 是一个命名空间,而OEList 之类的项目是前缀,它们存在于oneNote 创建的命名空间中,而您发布的片段中不存在该声明。存在前缀以避免在不同 XML 文档混合在一起的情况下发生命名冲突。 see a w3schools link for further explanation

因此,一种解决方法是将诸如 &lt;xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote"&gt; 之类的命名空间附加到每个片段(似乎不是最佳的,但它确实有效),然后像您所做的那样对它进行反序列化。

我没有方便的 OneNote,所以命名空间声明是 from this forum post

另一种反序列化 XML 片段的方法是通过 XMLReader 和 XMLReaderSettings,您可以在其中将 coformance 级别设置为 Fragment。并添加一个预定义的命名空间。

例子改编自this MSDN blog

 XmlDocument doc = new XmlDocument();

        NameTable nt = new NameTable();
        XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);
        nsmgr.AddNamespace("one", "http://schemas.microsoft.com/office/onenote/2010/onenote");
        XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
        XmlReaderSettings xset = new XmlReaderSettings();
        xset.ConformanceLevel = ConformanceLevel.Fragment;
        XmlReader rd = XmlReader.Create(new  StringReader(XMLSource), xset, context); 

        doc.Load(rd);

我个人更喜欢使用 XMLReader 和 XMLReader 设置,尽管它们似乎需要CreateReader() 并进行设置,但它看起来是一种更强大的方式。

或者,如果你不想处理自定义命名空间之类的,也不想遇到冲突的问题,只需以编程方式删除 one: 的前面声明,然后继续反序列化它。不过,这更多的是字符串操作。

【讨论】:

  • 题外话,但如果你想为片段设置默认命名空间,只需将它的 URI 添加到命名空间管理器,前缀为 String.Empty。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 2013-05-02
  • 1970-01-01
  • 2019-10-12
  • 1970-01-01
相关资源
最近更新 更多