从非常大的xml文件(可能已损坏)中提取第一个k子文件的简单方法



有没有一种简单的方法,可以使用XPath查询(或在每台linux/osx机器中都可以轻松找到的任何其他命令行工具)从大型XML文件中提取原始文件的子集?

具体来说,我有一个大的xml文件,格式为:

<root>
  <header>...<>
  <item name="1">...<>
  <item name="2">...<>
  ...
  <item name="1000000">..<>
</root>

并且我希望输出一个较小的XML文件,其中包括头的前k个项目(比如10个)。附带说明一下,请考虑文件可能已损坏。从本质上讲,我正在寻找一个类似于head的命令,该命令使用SAX解析器解析XML文件(为了不占用内存,并对文件的过早终止具有弹性)。

我认为xsl:iterate的流式处理允许在XSLT3.0中实现这一点,正如Saxon 9.7 EE目前所实现的那样(这显然不是一个在LINUX上很容易获得的命令行工具,但由于它确实解决了这个问题,我认为值得一提):假设一个格式不好的名为test2015122701.xml的XML,格式为

<root>
  <header>...</header>
  <item name="1">...</item>
  <item name="2">...</item>
  <item name="3">...</item>
  <item name="4">...</item>
  <item>
</root>

和带有代码的XSLT3.0样式表

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="xs">
<xsl:param name="items-to-copy" as="xs:integer" select="4"/>
<xsl:variable name="children-to-copy" as="xs:integer" select="$items-to-copy + 1"/>
<xsl:param name="input-uri" as="xs:string" select="'test2015122701.xml'"/>
<xsl:output indent="yes"/>
<xsl:template name="main" match="/">
  <root>
    <xsl:stream href="{$input-uri}">
      <xsl:iterate select="root/*">
        <xsl:copy-of select="."/>
        <xsl:if test="position() eq $children-to-copy">
          <xsl:break/>
        </xsl:if>
      </xsl:iterate>
    </xsl:stream>
  </root>
</xsl:template>
</xsl:stylesheet>

Saxon 9.7 EE,当与java -jar saxon9ee.jar -it:main -xsl:sheet.xsl一起运行时,会产生以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <header>...</header>
   <item name="1">...</item>
   <item name="2">...</item>
   <item name="3">...</item>
   <item name="4">...</item>
</root>

如果我们使用-t命令行选项来检查处理的一些细节,我们会看到:

Streaming file:/C:/Users/Martin%20Honnen/Documents/xslt/test2015122701.xml
URIResolver.resolve href="test2015122701.xml" base="file:/C:/Users/Martin%20Honnen/Documents/xslt/test2015122702.xsl"
Using parser com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser
Streaming test2015122701.xml : early exit

因此Saxon实际上只处理文件的开头,并在遇到第一个元素之后格式不好的标记之前退出。

作为替代方案,这里有一个使用Perl和XML::Twig:的示例

use strict;
use XML::Twig;
my $itemCount = 0;
my $breakCount = 4;
sub count_items {
    my ($t, $item) = @_;
    $itemCount++;
    if ($itemCount == $breakCount) {
      $t->finish_now();
    }
}
my $input = 'input.xml';
my $result = 'output.xml';

my $twig = XML::Twig->new(
  twig_handlers => { item => &count_items},
  pretty_print => 'indented'
);
$twig->parsefile($input);
$twig->print_to_file($result);

恐怕我不知道Perl和XML::Twig在LINUX上的支持程度,我已经测试了上面的内容,以便在带有XML::twg 3.49的Windows上使用Perl5.20.3。

相关内容

最新更新