关于C#:Xerces,xpath和XML命名空间

Xerces, xpaths, and XML namespaces

我正在尝试使用xerces-c来解析从StarUML生成的相当大的XML文档以更改某些内容,但是我遇到了使xpath查询正常工作的问题,因为它一直崩溃。

为简化起见,我将文件的一部分分成一个较小的XML文件进行测试,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<XPD:UNIT xmlns:XPD="http://www.staruml.com" version="1">
  <XPD:HEADER>
    <XPD:SUBUNITS>
    </XPD:SUBUNITS>
  </XPD:HEADER>
  <XPD:BODY>
    <XPD:OBJ name="Attributes[3]" type="UMLAttribute" guid="onMjrHQ0rUaSkyFAWtLzKwAA">
      <XPD:ATTR name="StereotypeName" type="string">ConditionInteraction</XPD:ATTR>
    </XPD:OBJ>
  </XPD:BODY>
</XPD:UNIT>

在此示例中,我要做的就是查找所有XPD:OBJ元素,其中只有一个。该问题似乎源于尝试使用名称空间进行查询。当我通过一个非常简单的xpath查询XPD:OBJ时,它将崩溃,但是如果我仅传递OBJ,它将不会崩溃,但不会找到XPD:OBJ元素。

我认为我需要设置一些重要的属性或设置,但我不知道它可能是什么。我查找了与名称空间有关的解析器的所有属性,并启用了那些我可以使用的属性,但是它根本没有帮助,所以我完全陷入了困境。初始化代码看起来像这样,明显地删除了很多东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const tXercesXMLCh tXMLManager::kDOMImplementationFeatures[] =
{
    static_cast<tXercesXMLCh>('L'),
    static_cast<tXercesXMLCh>('S'),
    static_cast<tXercesXMLCh>('\\0')
};

// Instantiate the DOM parser.
fImplementation = static_cast<tXercesDOMImplementationLS *>(tXercesDOMImplementationRegistry::getDOMImplementation(kDOMImplementationFeatures));

if (fImplementation != nullptr)
{
    fParser = fImplementation->createLSParser(tXercesDOMImplementationLS::MODE_SYNCHRONOUS, nullptr);
    fConfig = fParser->getDomConfig();

    // Let the validation process do its datatype normalization that is defined in the used schema language.
    //fConfig->setParameter(tXercesXMLUni::fgDOMDatatypeNormalization, true);

    // Ignore comments and whitespace so we don't get extra nodes to process that just waste time.
    fConfig->setParameter(tXercesXMLUni::fgDOMComments, false);
    fConfig->setParameter(tXercesXMLUni::fgDOMElementContentWhitespace, false);

    // Setup some properties that look like they might be required to get namespaces to work but doesn't seem to help at all.
    fConfig->setParameter(tXercesXMLUni::fgXercesUseCachedGrammarInParse, true);
    fConfig->setParameter(tXercesXMLUni::fgDOMNamespaces, true);
    fConfig->setParameter(tXercesXMLUni::fgDOMNamespaceDeclarations, true);

    // Install our custom error handler.
    fConfig->setParameter(tXercesXMLUni::fgDOMErrorHandler, &fErrorHandler);
}

然后稍后我解析文档,找到根节点,然后运行xpath查询以找到所需的节点。我将忽略其中的大部分内容,仅向您显示我在哪里运行xpath查询,以防万一那里有明显的错误:

1
2
3
4
5
tXercesDOMDocument * doc; // Comes from parsing the file.
tXercesDOMNode * contextNode; // This is the root node retrieved from the document.
tXercesDOMXPathResult * xPathResult;

doc->evaluate("XPD:OBJ", contextNode, nullptr, tXercesDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE), xPathResult);

evaluate()的调用在xerces的深处崩溃,我看不到它很清楚,但是从我看到的东西来看,有很多东西看起来已删除或未初始化,所以我没有确定到底是什么导致了崩溃。

那么在这里是否存在使xerces与XML名称空间一起使用所需的任何明显错误或遗漏的东西?


解决方案一直都在我的面前。问题是您需要创建一个解析器并将其传递给evaluate()调用,否则它将无法找出任何名称空间,并且将引发异常。崩溃似乎是xerces中的一个错误,因为它在无法解析名称空间时试图抛出异常而崩溃。我必须深入调试xerces代码才能找到它,这为我提供了解决方案。

因此,为了解决该问题,我将调用稍微更改为evaluate()以创建具有根节点的解析器,现在它可以正常运行:

1
2
3
4
5
6
7
8
9
10
11
12
tXercesDOMDocument * doc; // Comes from parsing the file.
tXercesDOMNode * contextNode; // This is the root node retrieved from the document.
tXercesDOMXPathResult * xPathResult;

// Create the resolver with the root node, which contains the namespace definition.
tXercesDOMXPathNSResolver * resolver(doc->createNSResolver(contextNode));

doc->evaluate("XPD:OBJ", contextNode, resolver, tXercesDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE), xPathResult);

// Make sure to release the resolver since anything created from a `create___()`
// function has to be manually released.
resolver->release();