【问题标题】:Process IMSManifest.xml with XPath and C#使用 XPath 和 C# 处理 IMSManifest.xml
【发布时间】:2013-08-30 13:29:46
【问题描述】:

我不熟悉 XPath,我正在寻找有关从文件中简单地选择三个值的指导:架构版本、标题和描述。

当我从<manifest><lom> 中去除名称间距信息时,Xpath 表达式//title/langstring 仅匹配元素值。

在内容中搜索这些值的正确方法是什么?

单元测试:

[Test]
public void TitleIsNotNull()
{
  var manifestManager = new ManifestManager("imsmanifest.xml");
  // Code which initializes object and calls GetTitle() is encapsulated.
  Assert.IsNotNullOrEmpty(manifestManager.Title);
}

被测系统:

private string GetTitle()
{
  var document = XElement.Parse(_contents);
  const string XpathExpression = "//title/langstring";
  return (string)document.XPathSelectElement(XpathExpression);
}

_contents(摘录):

<?xml version="1.0" encoding="utf-8"?>
<manifest xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2
  imscp_rootv1p1p2.xsd 
  http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd 
  http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2" 
  xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2" 
  version="1.0"
  identifier="ExampleIdGoesHere">
  <metadata>
    <schema>ADL SCORM</schema>
    <schemaversion>1.2</schemaversion>
    <lom xsi:schemaLocation="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1
               imsmd_rootv1p2p1.xsd" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns="http://www.imsglobal.org/xsd/imsmd_rootv1p2p1">
        <general>
            <title>
                <langstring xml:lang="x-none">Example title goes here.</langstring>
            </title>
            <description>
                <langstring xml:lang="x-none">Example description goes here.</langstring>
            </description>
        </general>
    </lom>
  </metadata>

根据 Steven Doggart 的解决方案调整代码

//已修改

private string GetTitle()
{
  var xmlReader = GetXmlReader();
  var document = XElement.Load(xmlReader);
  var xmlNamespaceManager = GetXmlNamespaceManager(xmlReader);
  const string XpathExpression = "//y:title/y:langstring";
  return (string)document.XPathSelectElement(XpathExpression, xmlNamespaceManager);
}

//私人助手

    private XmlReader GetXmlReader()
    {
        var contents = new StringReader(_contents);
        var xmlReader = XmlReader.Create(contents);
        return xmlReader;
    }

    private XmlNamespaceManager GetXmlNamespaceManager(XmlReader xmlReader)
    {
        if (xmlReader.NameTable != null)
        {
            var xmlNamespaceManager = new XmlNamespaceManager(xmlReader.NameTable);
            xmlNamespaceManager.AddNamespace("x", "http://www.imsproject.org/xsd/imscp_rootv1p1p2");
            xmlNamespaceManager.AddNamespace("y", "http://www.imsglobal.org/xsd/imsmd_rootv1p2p1");
            return xmlNamespaceManager;
        }

        return null;
    } 

【问题讨论】:

    标签: c# .net xml xpath


    【解决方案1】:

    您遇到的问题是您尝试选择的元素实际上属于某个命名空间,但您在选择它时没有指定命名空间。 titlelangstring 元素都属于默认命名空间。在 XML 文档中,默认命名空间定义为 "http://www.imsproject.org/xsd/imscp_rootv1p1p2"。使用 XPath,无法指定默认名称空间。如果您不提供命名空间,它总是假定您根本没有命名空间。因此,要选择该元素,您必须显式提供命名空间,如下所示:

    XmlNamespaceManager namespaceManager = new XmlNamespaceManager(nameTable);
    namespaceManager.AddNamespace("x", "http://www.imsproject.org/xsd/imscp_rootv1p1p2");
    const string XpathExpression = "//x:title/x:langstring";
    return (string)document.XPathSelectElement(XpathExpression, namespaceManager);
    

    然而,诀窍是让XmlNameTable 提供给XmlNamespaceManager。不幸的是,XElement 类没有提供获取文档的XmlNameTable 的方法,因此最好的办法是通过XmlReader 加载它,它可以提供,如下所示:

    XmlReader reader = XmlReader.Create(new StringReader(_contents));
    XElement document = XElement.Load(reader);
    XmlNamespaceManager namespaceManager = new XmlNamespaceManager(reader.NameTable);
    namespaceManager.AddNamespace("x", "http://www.imsproject.org/xsd/imscp_rootv1p1p2");
    const string XpathExpression = "//x:title/x:langstring";
    return (string)document.XPathSelectElement(XpathExpression, namespaceManager);
    

    或者,您可以使用XmlDocument,这在处理命名空间时会稍微容易一些。或者,您也可以选择使用 LINQ 来选择元素而不是 XPath。

    【讨论】:

    • 史蒂文我不确定我搞砸了什么,但值返回 null 并且我的测试失败。
    • 为了让它在我这边工作,我还必须删除 schemalocation 属性。尝试一下,看看它是否为您解决了问题。如果是这样,那就是问题的第二部分。
    • 我能够添加一个新的命名空间声明xmlNamespaceManager.AddNamespace("y", "http://www.imsglobal.org/xsd/imsmd_rootv1p2p1"); 来查找嵌套在&lt;lom&gt; 元素下的元素。我修改了我的问题以包括您的修复,并且我接受了您的回答。谢谢。
    猜你喜欢
    • 2019-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 2021-10-08
    • 1970-01-01
    相关资源
    最近更新 更多