Java 使用 XPath 解析 iTunes XML 库
Java Parsing iTunes XML library using XPath
所以我正在尝试创建一种允许我输入曲目 ID 的方法,然后 return 属于曲目 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 文件的复杂性和巨大的大小,我正在努力浏览它。
到目前为止,我的想法是导航到 <key>646</key>
以检查 ID,然后使用 "//dict/key[.="646"]/string[1]/text()"
.
导航到曲目名称 <string>Save Me</string>
这会产生 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 的建议产生了正确的结果,"Save Me" 符合预期的轨道 646。但是,当我输入另一个曲目 ID 时,它也会 returns "Save Me",这是不正确的。
我不知道为什么要这样做,因为我认为它只会 return 我输入的 ID 的曲目名称,但它 return 是一个不同的曲目名称?
第二次编辑:
-包含更多 XML
第三次编辑:
使用 Mathias 的建议将 XPath 表达式更改为 //dict[integer ="+id+"]/string[1]/text()
。这工作得很好。
我无法评论 Java 代码,但我可以向您解释您应该使用的 XPath 表达式。假设您显示的输入样本,应用
//dict[key='646']/dict/key[. = 'Name']/following-sibling::*[1]
将return
<string>Save Me</string>
这就是您要找的元素。要仅 select 其文本内容,请使用
//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 的方法,然后 return 属于曲目 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 文件的复杂性和巨大的大小,我正在努力浏览它。
到目前为止,我的想法是导航到 <key>646</key>
以检查 ID,然后使用 "//dict/key[.="646"]/string[1]/text()"
.
<string>Save Me</string>
这会产生 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 的建议产生了正确的结果,"Save Me" 符合预期的轨道 646。但是,当我输入另一个曲目 ID 时,它也会 returns "Save Me",这是不正确的。
我不知道为什么要这样做,因为我认为它只会 return 我输入的 ID 的曲目名称,但它 return 是一个不同的曲目名称?
第二次编辑:
-包含更多 XML
第三次编辑:
使用 Mathias 的建议将 XPath 表达式更改为 //dict[integer ="+id+"]/string[1]/text()
。这工作得很好。
我无法评论 Java 代码,但我可以向您解释您应该使用的 XPath 表达式。假设您显示的输入样本,应用
//dict[key='646']/dict/key[. = 'Name']/following-sibling::*[1]
将return
<string>Save Me</string>
这就是您要找的元素。要仅 select 其文本内容,请使用
//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()