XPath - выбор узла на основе следующего брата - PullRequest
3 голосов
/ 24 сентября 2011

У меня такой запрос:

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

Что я действительно хочу выбрать, так это ключевой узел (непосредственно перед следующим-sibling :: 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, если название города «другое».city ​​').

Я делаю это в 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();
            }
        }
    }
}

Похоже,nested follow-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?Решение работает во всех других редакторах пути.

Ответы [ 2 ]

5 голосов
/ 25 сентября 2011

Одно выражение 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>
1 голос
/ 27 сентября 2011

Чтобы получить номер для города, вы также можете использовать 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 и добавить к нему номер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...