【问题标题】:XPath - selecting node based on following siblingXPath - 根据以下兄弟选择节点
【发布时间】:2011-11-24 08:25:36
【问题描述】:

我有一个这样的查询:

/plist/dict[1]/dict/dict/dict/key/following-sibling::dict/string[string()='Algier']

我真正想要选择的是“关键”节点(就在下面的兄弟::dict 之前)之一。

xml是这样的:

<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
    <dict>
    <key>en_GB</key>
            <dict>
                <key>Africa</key>
                <dict>
                    <key>Algeria</key>
                    <dict>
                        <key>60390</key>
                        <dict>
                            <key>NAME</key>
                            <string>Algier</string>
                            <key>LATITUDE</key>
                            <string>36.7500</string>
                            <key>LONGITUDE</key>
                            <string>3.0500</string>
                        </dict>
                        <key>60391</key>
                        <dict>
                            <key>NAME</key>
                            <string>Some other city</string>
                            <key>LATITUDE</key>
                            <string>36.7500</string>
                            <key>LONGITUDE</key>
                            <string>3.0500</string>
                        </dict>
                    </dict>
                </dict>
        </dict>
    </dict>
</plist>

换句话说,当城市名称为“阿尔及尔”时,我想选择“60390”或(当城市名称为“其他城市”时选择 60391)。

我在 QML XmlListModel 中执行此操作。

更新代码 使用的 QML:

import QtQuick 1.0

Rectangle {
    id: container;
    width: 360
    height: 360

    function onLocationModelLoaded(){
        console.debug(weLocationModel.count);
    }

    XmlListModel{
        id: weLocationModel;
        source: "we_locations.plist";
        query: "/*/dict/dict/dict/dict/key[following-sibling::dict[1]/key[.='NAME' and following-sibling::string[1] = 'Algier']]"

        XmlRole{
            name: "cityId";
            query: "name()";
        }
        onStatusChanged: {
            if (status == XmlListModel.Ready){
                console.debug("Location Model Ready");
                container.onLocationModelLoaded();
            }
        }
    }
}

嵌套的 following-sibling 似乎不起作用。 例如:

query: "/*/dict/dict/dict/dict/key[following-sibling::dict[1]/key[.='NAME']]"

这两个总是返回:

Error XPST0003 in file:///Users/Ali/qtprojects/welocationtest-build-simulator/welocationtest.app/Contents/MacOS/welocationtest, at line 2, column 97: syntax error, unexpected ], expecting )
Error XPST0003 in file:///Users/Ali/qtprojects/welocationtest-build-simulator/welocationtest.app/Contents/MacOS/welocationtest, at line 2, column 91: syntax error, unexpected ], expecting end of file
file:///Users/Ali/qtprojects/welocationtest-build-simulator/welocationtest.app/Contents/Resources/qml/welocationtest/main.qml:17:9: QML XmlRole: invalid query: "name()"
Location Model Ready
0

有没有可能 QML 不遵循 XPath 标准?该解决方案适用于所有其他路径编辑器。

【问题讨论】:

  • 似乎 QML 有问题且不合规......
  • 难怪诺基亚是现在的位置。 :(
  • 我查看了这个,我同意,如果 QML 在给定的 XPath 表达式上抛出上述错误,QML 有一个错误。当没有( 时,为什么它会期待)?你能提交错误报告吗?
  • 似乎错误是针对:query: "name()"; 。您实际上想在此处指定什么?

标签: xpath qml


【解决方案1】:

一个 XPath 表达式可以准确选择所需的 key 元素

   /*/dict/dict/dict/dict
     /key
        [following-sibling::dict[1]/key
                  [.='NAME'
                 and
                   following-sibling::string[1] = $pCity
                  ]
        ]

当 $pCity 被 "Algier" 设置/替换时,此 XPath 表达式选择

<key>60390</key>

当 $pCity 被 "Some other city" 设置/替换时,此 XPath 表达式选择

<key>60391</key>

基于 XSLT 的验证

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pCity" select="'Some other city'"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "/*/dict/dict/dict/dict
     /key
        [following-sibling::dict[1]/key
                  [.='NAME'
                 and
                   following-sibling::string[1] = $pCity
                  ]
        ]
  "/>
 </xsl:template>
</xsl:stylesheet>

当对提供的 XML 文档应用此转换时

<plist version="1.0">
    <dict>
        <key>en_GB</key>
        <dict>
            <key>Africa</key>
            <dict>
                <key>Algeria</key>
                <dict>
                    <key>60390</key>
                    <dict>
                        <key>NAME</key>
                        <string>Algier</string>
                        <key>LATITUDE</key>
                        <string>36.7500</string>
                        <key>LONGITUDE</key>
                        <string>3.0500</string>
                    </dict>
                    <key>60391</key>
                    <dict>
                        <key>NAME</key>
                        <string>Some other city</string>
                        <key>LATITUDE</key>
                        <string>36.7500</string>
                        <key>LONGITUDE</key>
                        <string>3.0500</string>
                    </dict>
                </dict>
            </dict>
        </dict>
    </dict>
</plist>

产生想要的正确结果

<key>60391</key>

当在上述转换中我们替换

 <xsl:param name="pCity" select="'Some other city'"/>

 <xsl:param name="pCity" select="'Algier'"/>

并再次应用转换,然后我们再次得到正确的结果

<key>60390</key>

【讨论】:

  • 嗯,听起来很合理。虽然我必须在 QML 中检查它才能真正将其标记为正确答案。同时我会投票,因为似乎正在使用其他工具。 :) 非常感谢。
  • @debuggerman:不客气。如果 QML 实现了 XPath(因为它应该),它应该使用这个解决方案产生想要的结果。
【解决方案2】:

要获取城市的号码,您也可以使用 RegEx(特别是如果 QML XPath 支持不够好):

import QtQuick 1.0

Rectangle {
    id: container

    width: 360
    height: 360

    Component.onCompleted: {
        loadWeLocationsFile();
    }

    property string weLocationsXML
    signal weLocationsLoaded()

    function loadWeLocationsFile() {
        // load file
        var doc = new XMLHttpRequest();
        doc.onreadystatechange = function() {
            if (doc.readyState == XMLHttpRequest.DONE) {
                // get file content
                weLocationsXML = doc.responseText;

                // emit signal
                weLocationsLoaded();
            }
        }

        doc.open("GET", "we_locations.plist");
        doc.send();
    }

    function getIDByName(name) {
        // escape special characters for regex (maybe there is a better way to do this)
        var safeName = name.replace(/[-.,?+*#^$()[\]{}\\|]/g, "\\$&");

        // create regex
        var regex = new RegExp("<key>(.*?)</key>\\s*<dict>\\s*<key>NAME</key>\\s*<string>" + safeName + "</string>", "m");

        // execute regex
        var match = regex.exec(weLocationsXML);

        if (match != null && match.length > 1) {
            return match[1];  // name found, group 1 contains id
        } else {
            return null;  // no such name in XML
        }
    }

    // Test it ...
    onWeLocationsLoaded: {
        console.log("Number for 'Algier':", getIDByName("Algier"));
        console.log("Number for 'NotInXML':", getIDByName("NotInXML"));
        console.log("Number for 'Some other city':", getIDByName("Some other city"));
    }
}

输出:

Number for 'Algier': 60390
Number for 'NotInXML': null
Number for 'Some other city': 60391

如果您需要 QML 模型中的号码,您可以创建 ListModel 并将号码添加到其中。

【讨论】:

  • 太棒了。非常感谢 :)。暂时解决了我的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多