使用 XPath 和 C# 处理 IMSManifest.xml

本文关键字:IMSManifest xml 处理 XPath 使用 | 更新日期: 2023-09-27 18:29:35

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

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

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

单元测试:

[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;
    } 

使用 XPath 和 C# 处理 IMSManifest.xml

您遇到的问题是,您尝试选择的元素实际上属于某个命名空间,但是您在选择它时没有指定命名空间。 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);

然而,诀窍是让XmlNameTableXmlNamespaceManager . 不幸的是,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 来选择元素。