完成XML转换的最有效方式-Java

时间:2019-07-18 20:01:13

标签: java dom xpath xml-parsing

我是Java的新手,我希望对社区提出意见。 我有一个巨大的XML,其中包含很多信息。实际上,此XML具有大约140Mb的信息。 在此XML中,我有很多不再有效的信息,因此我需要进行过滤并仅使用有效的信息,要进行检查,我需要在节点之间交叉信息,以检查是否需要删除。在某些情况下,需要删除整个父(主)节点。

我已经使用dom解析,使用循环,在循环内部执行了此操作,我保存了变量并交叉了信息以进行检查,并删除了实际节点或整个父节点。

基本上,结构是这样的:

<source>
    <main>
        <id>98567</id>
        <block_information>
            <name>Block A</name>
            <start_date>20120210</start_date>
            <end_date>20150210</end_date>
        </block_information>
        <block_information>
            <name>Block A.01</name>
            <start_date>20150210</start_date>
            <end_date>20251005</end_date>
        </block_information>
        <city_information>
            <name>Manchester</name>
            <start_date>20150210</start_date>
            <end_date>20150212</end_date>
        </city_information>
        <city_information>
            <name>New Manchester</name>
            <start_date>20150212</start_date>
            <end_date>20251005</end_date>
        </city_information>
        <phone>
            <type>C</type>
            <number>987466321</number>
            <name></name>
        </phone>
        <phone>
            <type>P</type>
            <number>36547821</number>
            <name></name>
        </phone>
    </main>
    <main>
        <id>19587</id>
        <block_information>
            <name>Che</name>
            <start_date>20090210</start_date>
            <end_date>20100210</end_date>
        </block_information>
        <block_information>
            <name></name>
            <start_date>20100210</start_date>
            <end_date>20351005</end_date>
        </block_information>
        <city_information>
            <name></name>
            <start_date>20150210</start_date>
            <end_date>20150212</end_date>
        </city_information>
        <city_information>
            <name>No Name</name>
            <start_date>20150212</start_date>
            <end_date>20191005</end_date>
        </city_information>
        <phone>
            <type>C</type>
            <number>987466321</number>
            <name>Mom</name>
        </phone>
        <phone>
            <type>P</type>
            <number>36547821</number>
            <name></name>
        </phone>
    </main>
</source>

输出如下:

<result>
        <main>
                <id>98567</id>
                <block_name>Block A.01</block_name>
                <city_name>New Manchester</city_name>
                <cellphone></cellphone>
                <phone>36547821</phone>
                <contact_phone></contact_phone>
                <contact_phone_name></contact_phone_name>
        </main>
</result>

要使结果有效,必须强制有一个<block_information><city_information>有效(<start_date>比实际日期少,<end_date>比实际日期大) ,并且两者都需要<name...>。 如果没有任何一个或有效的多个,则<main>将被删除。

对于电话号码,<type> [“ C”代表联系人,“ P”代表个人电话,“ M”代表移动电话]。因此,如果<type>为'C',但<name>中没有值,则电话不会得到结果。 'P'转到<phone>,'M'转到<cellphone>

我希望您考虑以最佳的方式执行此操作的最佳方法,如果需要的话,任何人都可以轻松地进行调整。

提前感谢您的投入!

按照@kjhughes的要求,我在示例XML上添加了一些值,并需要做一些过滤器。谢谢!

ps .:作为示例的XML结构与实际相比太简单了,有很多复杂的类型。

2 个答案:

答案 0 :(得分:0)

我会采用以下方法:

  • 找到一个库,可让您流式传输xml(文件或inputsream)并生成Stream<Main>
  • 根据您的验证逻辑处理Stream<Main>并过滤每个Main节点
  • 根据您是I / O还是CPU瓶颈,使用.parallel()流来处理该流(请阅读:测试.parallel()是否以任何方式对您有帮助)

这将满足XML解析上下文中任何合理的性能要求(我想是吗?)。 Google for Java XML Stream并从那里开始(或者也许this stackoverflow问题可以提供一些指导)

答案 1 :(得分:0)

XSLT是一种自1999年以来一直存在的转换语言,现在具有三个版本,即1.0、2.0和3.0,这是最新版本,于2017年作为W3C建议发布,并在Saxon 9.8和更高版本的Java平台上受支持,可在开放平台上获得:在Sourceforge和Maven上获得HE版本。通过合并Apache Xalan,Oracle / Sun Java JRE支持使用XSLT 1。

因此,可以选择使用XSLT来代替使用DOM,这是使用XSLT 3(在https://xsltfiddle.liberty-development.net/bFN1yab/0上在线)的示例:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:output indent="yes"/>

  <xsl:function name="mf:date" as="xs:date">
      <xsl:param name="input-date" as="xs:string"/>
      <xsl:sequence
         select="xs:date(replace($input-date, '([0-9]{4})([0-9]{2})([0-9]{2})', '$1-$2-$3'))"/>
  </xsl:function>

  <xsl:function name="mf:select-valid-info" as="element()*">
      <xsl:param name="infos" as="element()*"/>
      <xsl:sequence
         select="$infos[name/normalize-space()
                       and mf:date(start_date) lt current-date()
                       and mf:date(end_date) gt current-date()]"/>
  </xsl:function>

  <xsl:function name="mf:valid-main" as="xs:boolean">
      <xsl:param name="main" as="element(main)"/>
      <xsl:sequence
        select="let $valid-blocks := mf:select-valid-info($main/block_information),
                    $valid-cities := mf:select-valid-info($main/city_information)
                return count($valid-blocks) eq 1 and count($valid-cities) eq 1"/>
  </xsl:function>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="main[not(mf:valid-main(.))]"/>

  <xsl:template match="main[mf:valid-main(.)]">
      <xsl:copy>
          <xsl:apply-templates 
            select="id,
                    mf:select-valid-info(block_information)/name,
                    mf:select-valid-info(city_information)/name,
                    phone"/>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="block_information/name | city_information/name">
      <xsl:element name="{substring-before(local-name(..), '_')}_name">
          <xsl:value-of select="."/>
      </xsl:element>
  </xsl:template>

  <xsl:template match="main/phone[type = 'C']">
      <contact_phone>
          <xsl:value-of select="number[current()/normalize-space(name)]"/>
      </contact_phone>
      <contact_name>
          <xsl:value-of select="name"/>
      </contact_name>
  </xsl:template>

  <xsl:template match="main/phone[type = 'P']">
      <phone>
          <xsl:value-of select="number"/>
      </phone>
  </xsl:template>

  <xsl:template match="main/phone[type = 'M']">
      <cellphone>
          <xsl:value-of select="number"/>
      </cellphone>
  </xsl:template>

</xsl:stylesheet>

我希望我已经掌握了main元素的条件,虽然我还不太了解各种电话数据的规则,但是无论如何,该代码还是作为示例。

当然,性能很大程度上取决于实现,但是我认为XSLT是一种比DOM编码更加结构化和可维护的方式。

如果您负担得起的话,还可以研究支持流式XSLT 3的Saxon 9.8或9.9 EE,通过重写上面的代码,您可以使用基于XSLT的方法仅通过巨大的文档进行流式转发,具体实现{ {1}}元素是您要转换的元素节点,同时保持较低的内存占用,与DOM或常规XSLT处理相比,这种方法不会首先将整个XML文档解析为完整的内存树结构:

main