使用 JAXB 和 XMLStreamReader 高效解组大型 xml 文件的一部分
Efficiently unmarshaling a part of a large xml file with JAXB and XMLStreamReader
我想解组一个大 XML 文件的一部分。已经有解决方案,但我想改进它以实现我自己的实现。
请看下面的代码:(source)
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(!xsr.getLocalName().equals("VersionList")&&xsr.getElementText().equals("1.81")) {
xsr.nextTag();
}
我想解组节点的 input.xml(如下所示):versionNumber="1.81"
使用当前代码,XMLStreamReader 将首先检查节点 versionNumber="1.80",然后检查 versionNumber 的所有子节点,然后再次移动到节点:versionNumber="1.81" ",满足while循环的退出条件。
因为,我只想检查节点版本号,迭代它的子节点是不必要的,对于大 xml 文件,迭代版本 1.80 的所有子节点将花费很长时间。我只想检查根节点(versionNumber),如果第一个根节点(versionNumber=1.80)不匹配,XMLStreamReader 应该直接跳转到下一个根节点((versionNumber=1.81))。但是用 xsr.nextTag() 似乎无法实现。有什么方法可以只遍历所需的根节点吗?
input.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fileVersionListWrapper FileName="src.h">
<VersionList versionNumber="1.80">
<Reviewed>
<commentId>v1.80(c5)</commentId>
<author>Robin</author>
<lines>47</lines>
<lines>48</lines>
<lines>49</lines>
</Reviewed>
<Reviewed>
<commentId>v1.80(c6)</commentId>
<author>Sujan</author>
<lines>82</lines>
<lines>83</lines>
<lines>84</lines>
<lines>85</lines>
</Reviewed>
</VersionList>
<VersionList versionNumber="1.81">
<Reviewed>
<commentId>v1.81(c4)</commentId>
<author>Robin</author>
<lines>47</lines>
<lines>48</lines>
<lines>49</lines>
</Reviewed>
<Reviewed>
<commentId>v1.81(c5)</commentId>
<author>Sujan</author>
<lines>82</lines>
<lines>83</lines>
<lines>84</lines>
<lines>85</lines>
</Reviewed>
</VersionList>
</fileVersionListWrapper>
您可以使用 XPATH
从 xml 获取节点
XPath,XML 路径语言,是一种用于从 XML 文档中选择节点的查询语言。此外,XPath 可用于根据 XML 文档的内容计算值(例如,字符串、数字或布尔值)。 What is Xpath.
您的 XPath 表达式将是
/fileVersionListWrapper/VersionList[@versionNumber='1.81']
意味着您只想 return 属性为 1.81
的 VersionList
JAVA代码
我假设您将 xml 作为字符串,因此您需要以下想法
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
Document document = builder.parse(inputSource);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("/fileVersionListWrapper/VersionList[@versionNumber='1.81']");
NodeList nl = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
现在它将简单地遍历每个节点
for (int i = 0; i < nl.getLength(); i++)
{
System.out.println(nl.item(i).getNodeName());
}
要使节点恢复到 xml,您必须创建一个新文档并将节点附加到它。
Document newXmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = newXmlDocument.createElement("fileVersionListWrapper");
for (int i = 0; i < nl.getLength(); i++)
{
Node node = nl.item(i);
Node copyNode = newXmlDocument.importNode(node, true);
root.appendChild(copyNode);
}
newXmlDocument.appendChild(root);
获得新文档后,您将 运行 序列化程序以获取 xml。
DOMImplementationLS domImplementationLS = (DOMImplementationLS) document.getImplementation();
LSSerializer lsSerializer = domImplementationLS.createLSSerializer();
String string = lsSerializer.writeToString(document);
现在您有了字符串 xml ,我假设您已经有了一个看起来类似于此的 Jaxb 对象
@XmlRootElement(name = "fileVersionListWrapper")
public class FileVersionListWrapper
{
private ArrayList<VersionList> versionListArrayList = new ArrayList<VersionList>();
public ArrayList<VersionList> getVersionListArrayList()
{
return versionListArrayList;
}
@XmlElement(name = "VersionList")
public void setVersionListArrayList(ArrayList<VersionList> versionListArrayList)
{
this.versionListArrayList = versionListArrayList;
}
}
您将简单地使用 Jaxb 解组器为您创建对象
JAXBContext jaxbContext = JAXBContext.newInstance(FileVersionListWrapper .class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
FileVersionListWrapper fileVersionListWrapper = (FileVersionListWrapper) jaxbUnmarshaller.unmarshal(reader);
我想解组一个大 XML 文件的一部分。已经有解决方案,但我想改进它以实现我自己的实现。
请看下面的代码:(source)
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(!xsr.getLocalName().equals("VersionList")&&xsr.getElementText().equals("1.81")) {
xsr.nextTag();
}
我想解组节点的 input.xml(如下所示):versionNumber="1.81"
使用当前代码,XMLStreamReader 将首先检查节点 versionNumber="1.80",然后检查 versionNumber 的所有子节点,然后再次移动到节点:versionNumber="1.81" ",满足while循环的退出条件。
因为,我只想检查节点版本号,迭代它的子节点是不必要的,对于大 xml 文件,迭代版本 1.80 的所有子节点将花费很长时间。我只想检查根节点(versionNumber),如果第一个根节点(versionNumber=1.80)不匹配,XMLStreamReader 应该直接跳转到下一个根节点((versionNumber=1.81))。但是用 xsr.nextTag() 似乎无法实现。有什么方法可以只遍历所需的根节点吗?
input.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<fileVersionListWrapper FileName="src.h">
<VersionList versionNumber="1.80">
<Reviewed>
<commentId>v1.80(c5)</commentId>
<author>Robin</author>
<lines>47</lines>
<lines>48</lines>
<lines>49</lines>
</Reviewed>
<Reviewed>
<commentId>v1.80(c6)</commentId>
<author>Sujan</author>
<lines>82</lines>
<lines>83</lines>
<lines>84</lines>
<lines>85</lines>
</Reviewed>
</VersionList>
<VersionList versionNumber="1.81">
<Reviewed>
<commentId>v1.81(c4)</commentId>
<author>Robin</author>
<lines>47</lines>
<lines>48</lines>
<lines>49</lines>
</Reviewed>
<Reviewed>
<commentId>v1.81(c5)</commentId>
<author>Sujan</author>
<lines>82</lines>
<lines>83</lines>
<lines>84</lines>
<lines>85</lines>
</Reviewed>
</VersionList>
</fileVersionListWrapper>
您可以使用 XPATH
从 xml 获取节点XPath,XML 路径语言,是一种用于从 XML 文档中选择节点的查询语言。此外,XPath 可用于根据 XML 文档的内容计算值(例如,字符串、数字或布尔值)。 What is Xpath.
您的 XPath 表达式将是
/fileVersionListWrapper/VersionList[@versionNumber='1.81']
意味着您只想 return 属性为 1.81
的 VersionListJAVA代码
我假设您将 xml 作为字符串,因此您需要以下想法
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource inputSource = new InputSource(new StringReader(xml));
Document document = builder.parse(inputSource);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile("/fileVersionListWrapper/VersionList[@versionNumber='1.81']");
NodeList nl = (NodeList) expr.evaluate(document, XPathConstants.NODESET);
现在它将简单地遍历每个节点
for (int i = 0; i < nl.getLength(); i++)
{
System.out.println(nl.item(i).getNodeName());
}
要使节点恢复到 xml,您必须创建一个新文档并将节点附加到它。
Document newXmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = newXmlDocument.createElement("fileVersionListWrapper");
for (int i = 0; i < nl.getLength(); i++)
{
Node node = nl.item(i);
Node copyNode = newXmlDocument.importNode(node, true);
root.appendChild(copyNode);
}
newXmlDocument.appendChild(root);
获得新文档后,您将 运行 序列化程序以获取 xml。
DOMImplementationLS domImplementationLS = (DOMImplementationLS) document.getImplementation();
LSSerializer lsSerializer = domImplementationLS.createLSSerializer();
String string = lsSerializer.writeToString(document);
现在您有了字符串 xml ,我假设您已经有了一个看起来类似于此的 Jaxb 对象
@XmlRootElement(name = "fileVersionListWrapper")
public class FileVersionListWrapper
{
private ArrayList<VersionList> versionListArrayList = new ArrayList<VersionList>();
public ArrayList<VersionList> getVersionListArrayList()
{
return versionListArrayList;
}
@XmlElement(name = "VersionList")
public void setVersionListArrayList(ArrayList<VersionList> versionListArrayList)
{
this.versionListArrayList = versionListArrayList;
}
}
您将简单地使用 Jaxb 解组器为您创建对象
JAXBContext jaxbContext = JAXBContext.newInstance(FileVersionListWrapper .class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(xmlString);
FileVersionListWrapper fileVersionListWrapper = (FileVersionListWrapper) jaxbUnmarshaller.unmarshal(reader);