【问题标题】:Java Parsing iTunes XML library using XPathJava 使用 XPath 解析 iTunes XML 库
【发布时间】:2015-06-17 08:01:39
【问题描述】:

所以我正在尝试创建一个方法,允许我输入轨道 ID,然后返回属于轨道 ID 的轨道名称。

我需要使用 XPath 将 XML 文档解析成 java,然后再序列化一个新的库。我的 XML 文档示例在这里:

<plist version="1.0">
    <dict>
        <key>Major Version</key>
        <integer>1</integer>
        <key>Minor Version</key>
        <integer>1</integer>
        <key>Date</key>
        <date>2015-03-16T15:04:23Z</date>
        <key>Application Version</key>
        <string>12.1.0.71</string>
        <key>Features</key>
        <integer>5</integer>
        <key>Show Content Ratings</key>
        <true/>
        <key>Music Folder</key>
        <string>
        file://localhost/C:/Users/Mark/Music/iTunes/iTunes%20Media/
        </string>
        <key>Library Persistent ID</key>
        <string>3B01AE08EA513C21</string>
        <key>Tracks</key>
        <dict>
            <key>646</key>
            <dict>
            <key>Track ID</key>
            <integer>646</integer>
            <key>Name</key>
            <string>Save Me</string>
            <key>Artist</key>
            <string>Avenged Sevenfold</string>
            <key>Album Artist</key>
            <string>Avenged Sevenfold</string>
            <key>Album</key>
            <string>Nightmare</string>
            <key>Genre</key>
            <string>Metal</string>
            <key>Kind</key>
            <string>MPEG audio file</string>
            <key>Size</key>
            <integer>23257166</integer>
            <key>Total Time</key>
            <integer>656535</integer>
            <key>Disc Number</key>
            <integer>1</integer>
            <key>Disc Count</key>
            <integer>1</integer>
            <key>Track Number</key>
            <integer>11</integer>
            <key>Track Count</key>
            <integer>11</integer>
            <key>Year</key>
            <integer>2010</integer>
            <key>Date Modified</key>
            <date>2012-10-21T22:07:20Z</date>
            <key>Date Added</key>
            <date>2012-10-21T22:07:20Z</date>
            <key>Bit Rate</key>
            <integer>276</integer>
            <key>Sample Rate</key>
            <integer>44100</integer>
            <key>Play Count</key>
            <integer>2</integer>
            <key>Play Date</key>
            <integer>3415934327</integer>
            <key>Play Date UTC</key>
            <date>2012-03-30T06:38:47Z</date>
            <key>Artwork Count</key>
            <integer>1</integer>
            <key>Persistent ID</key>
            <string>0000000000001389</string>
            <key>Track Type</key>
            <string>File</string>
            <key>Location</key>
            <string>
            file://localhost/C:/Users/Mark/Music/Avenged%20Sevenfold/Nightmare/11%20-%20Save%20Me.mp3
            </string>
            <key>File Folder Count</key>
            <integer>2</integer>
            <key>Library Folder Count</key>
            <integer>1</integer>
        </dict>
        <key>648</key>
        <dict>
            <key>Track ID</key>
            <integer>648</integer>
            <key>Name</key>
            <string>Welcome 2 Hell</string>
            <key>Artist</key>
            <string>Bad Meets Evil</string>
            <key>Album Artist</key>
            <string>Bad Meets Evil</string>
            <key>Composer</key>
            <string>Havoc, Magnedo7</string>
            <key>Album</key>
            <string>Hell: The Sequel (Deluxe Edition)</string>
            <key>Genre</key>
            <string>Rap</string>
            <key>Kind</key>
            <string>MPEG audio file</string>
            <key>Size</key>
            <integer>7467977</integer>
            <key>Total Time</key>
            <integer>177606</integer>
            <key>Track Number</key>
            <integer>1</integer>
            <key>Year</key>
            <integer>2011</integer>
            <key>Date Modified</key>
            <date>2012-10-21T22:07:20Z</date>
            <key>Date Added</key>
            <date>2012-10-21T22:07:20Z</date>
            <key>Bit Rate</key>
            <integer>320</integer>
            <key>Sample Rate</key>
            <integer>44100</integer>
            <key>Play Count</key>
            <integer>3</integer>
            <key>Play Date</key>
            <integer>3424485861</integer>
            <key>Play Date UTC</key>
            <date>2012-07-07T06:04:21Z</date>
            <key>Skip Count</key>
            <integer>2</integer>
            <key>Skip Date</key>
            <date>2012-11-26T14:02:44Z</date>
            <key>Artwork Count</key>
            <integer>1</integer>
            <key>Persistent ID</key>
            <string>000000000000138A</string>
            <key>Track Type</key>
            <string>File</string>
            <key>Location</key>
            <string>
            file://localhost/C:/Users/Mark/Music/Bad%20Meets%20Evil/Hell_%20The%20Sequel%20(Deluxe%20Edition)/01%20-%20Welcome%202%20Hell.mp3
            </string>
            <key>File Folder Count</key>
            <integer>2</integer>
            <key>Library Folder Count</key>
            <integer>1</integer>
        </dict>
</plist>

现在,总的来说,我对 XPath 和 XML 还很陌生,并且由于 iTunes XML 文件的复杂性和庞大的大小,我正在努力导航它。

到目前为止,我的想法是导航到 &lt;key&gt;646&lt;/key&gt; 以检查 id,然后使用 "//dict/key[.="646"]/string[1]/text()" 导航到轨道名称 &lt;string&gt;Save Me&lt;/string&gt;

这会产生 NULL。到目前为止,我用 Java 编写的代码是:

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class XMLparse {

    public XMLparse(){
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder;
        Document doc = null;
        try {
            builder = factory.newDocumentBuilder();
            doc = builder.parse(new File("C:\\musicLibrary.xml"));

            // Create XPathFactory object
            XPathFactory xpathFactory = XPathFactory.newInstance();

            // Create XPath object
            XPath xpath = xpathFactory.newXPath();

            int id = 646;

            String name = getTrackNameById(doc, xpath, id);
            System.out.println("Track Name with ID " + id + ": " + name);

        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }

    }

    public static String getTrackNameById(Document doc, XPath xpath, int id) {
        String name = null;
        try {
            XPathExpression expr = xpath.compile("//dict/integer[.="+id+"]/string[1]/text()");
            name = (String) expr.evaluate(doc, XPathConstants.STRING);
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }

        return name;
    }

}

任何帮助将不胜感激。

编辑:

使用 Mathias Müller 的建议产生了正确的结果,如预期的 646 号轨道的“Save Me”。但是,当我输入另一个曲目 ID 时,它也会返回“Save Me”,这是不正确的。

我不知道它为什么会这样做,因为我认为它只会返回我输入的 ID 的曲目名称,但它会返回不同的曲目名称?

第二次编辑:

-包含更多的 XML

第三次编辑:

使用 Mathias 的建议将 XPath 表达式更改为 //dict[integer ="+id+"]/string[1]/text()。这是完美的工作。

【问题讨论】:

    标签: java xml parsing xpath itunes


    【解决方案1】:

    我无法评论 Java 代码,但我可以向您解释您应该使用的 XPath 表达式。假设您显示的输入样本,应用

    //dict[key='646']/dict/key[. = 'Name']/following-sibling::*[1]
    

    会回来

    <string>Save Me</string>
    

    这是您要查找的元素。要仅选择其文本内容,请使用

    //dict[key='646']/dict/key[. = 'Name']/following-sibling::*[1]/text()
    

    结果是

    Save Me
    

    路径表达式的作用如下:

    //dict                    select `dict` elements anywhere in the document
    [key='646']               but only if they have an immediate child `key` whose text
                              content is equal to "646"
    /dict                     select their child elements called `dict`
    /key[. = 'Name']          of those `dict` elements select their child elements `key`,
                              but only if their text content is equal to "Name"
    /following-sibling::*[1]  of those `key` elements, select the first following sibling
                              element
    /text()                   and select its text content
    

    您的原始表达式,依赖于 string 元素的位置,也适用于微小的变化:

    //dict[key ="646"]/dict/string[1]/text()
    

    【讨论】:

    • 您的解决方案部分有效,解释得很好,但是当我搜索其他轨道 ID 时,它仍然返回轨道名称为“Save Me”,而它不应该。你有什么建议吗?
    • @user3068854 当然可以,但是您需要显示更多输入文档。包含更多个曲目的样本。我假设您没有硬编码表达式中的 ID 值?
    • 更新了 XML 以显示前两个曲目。我尝试对给出相同不需要结果的 ID 进行硬编码。请原谅任何不正确的缩进,该文件对我来说很难正确阅读。
    • @user3068854 不,我的意思是对 ID 值进行硬编码是错误的;)。您的新 XML 示例格式不正确,也不清楚应该如何更正,但我提出的表达式现在很可能会返回 two 结果,这两个结果都是轨道名称字符串,而不管身份证。如果您仍然需要帮助,请让您的示例格式正确(即在正确的位置正确关闭 dict 元素)。
    • 再次感谢您的帮助,我设法通过在其他地方搜索曲目 ID 并使用您提供的语法来识别曲目名称。只是我,还是 iTunes Library XML 文件真的很难阅读?我的图书馆有大约 9000 首曲目,我努力寻找 dict 元素的结尾以及新元素的开始位置。也许我生成的文件不正确?
    猜你喜欢
    • 2013-02-20
    • 2014-05-10
    • 2013-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    • 2012-07-02
    • 1970-01-01
    相关资源
    最近更新 更多