Java面向对象设计处理大型XML

8
我们正在设计一个处理XML消息的系统。
处理Java类需要从大型XML中分离出各种属性和值,并将其作为参数传递给各个处理程序类以进行不同的操作。
我们考虑了以下选项:
A)
将整个XML传递给每个处理程序,并让其提取相关部分,但觉得这可能会导致每次传递XML时效率低下。
B)
将XML转换为DTO或一组较小的DTO,并将每个DTO传递给相关的处理程序。
C)
将XML切成片段并将其传递给每个处理程序方法。
我们对这些选项都不满意,所以有什么建议呢?
示例XML
 <IdAction>supplied</IdAction>
    <RegId>true</RegId>
    <DeRegId>false</DeRegId>
    <SaveMessage>false</SaveMessage>
    <ServiceName>abcRequest</ServiceName>
    <timeToPerform>3600</timeToPerform>
    <timeToReceipt/>
    <SendToBES>true</SendToBES>
    <BESQueueName>com.abc.gateway.JMSQueue.forAddRequest</BESQueueName>
    <BESTransform/>
    <BESJMSProperties>
        <property>
            <propName>stateCode</propName>
            <propValue>OK</propValue>
        </property>
    <property>
            <propName>stateResponse</propName>
            <propValue>OK</propValue>
        </property>
    </BESJMSProperties>

这里有4个块,由4个处理程序处理,其中一个执行。
 <IdAction>supplied</IdAction>
    <RegId>true</RegId>
    <DeRegId>false</DeRegId>

另一个做法
<timeToPerform>3600</timeToPerform>
    <timeToReceipt/>

下一个是什么意思?
<SendToBES>true</SendToBES>
    <BESQueueName>com.abc.gateway.JMSQueue.forAddRequest</BESQueueName>
    <BESTransform/>
    <BESJMSProperties>
        <property>
            <propName>stateCode</propName>
            <propValue>OK</propValue>
        </property>
    <property>
            <propName>stateResponse</propName>
            <propValue>OK</propValue>
        </property>
    </BESJMSProperties>

以及其他类似的事情


@Jeremy:我们需要从 XML 中提取多个片段并将其制作成一个 DTO 对象 - 这是否容易实现? - romesub
你能提供关于你的文档结构更多的细节吗?例如,是否有一个常见的消息头需要解析以确定适当的处理程序,而正文是在一个节点下还是多个兄弟节点下? - bdoughan
@Blaise Doughan:没有共同的头文件,所有序列都是相同的,我们只是尝试将处理过程模块化为不同的处理程序。附有示例。 - romesub
这些 XML 消息有多大?它们非常巨大,以至于您希望避免同时将它们全部保存在内存中吗? - ilcavero
@ilcavero:每个消息平均大小为8-10 kb,但应用程序在高峰期必须处理每秒100条消息。总堆大小为2 GB,通常会很快填满。 - romesub
4个回答

6

在我看来,B听起来是最好的选择。A效率最低下,C可能需要进行一次解析并挑选出片段,然后进行第二轮处理才能正确处理它们?

使用SAX解析出最小的DTO集合以传输给专用处理器类。

顺便说一句,这是一个好问题。提前思考这些问题,并获得第二、第三、第四个意见是很好的 :-)


3
尝试过了吗? http://simple.sourceforge.net/ 个人建议为xml创建一个数据模型并传递此数据模型。请查看教程。通过自定义数据模型,您可以将所需的数据映射到模型中,并且对于处理程序类,您可以传递子节点或者xml数据模型的子集而不是整个xml数据模型。
如果您有以下结构的xml:
<book>
  <title>XML</title>
  <author>
     <firstname>John</firstname>
     <lastname>Doe</lastname>
  </author>
  <isbn>123541356eas</isbn>
</book>

然后您将拥有以下类似于此的数据模型:
[    Book     ]         [      Author    ]
---------------         ------------------
|String title |         |String firstname|
|String isbn  |         |String lastname |
|Author author| ------->|----------------|
---------------        

当Book有一个指向Author的引用时,您可以将Author对象传递给处理程序方法。


3
我认为您在内存使用和性能方面不需要特别考虑设计,因此我建议采用编码最少的解决方案,即使用JAXB marshaller将您的XML解析为DTO,然后采用计划B。也许它比StAX难设置,但它可以避免您编写任何XML解析代码。

http://jaxb.java.net/

如果您正在使用Spring,设置org.springframework.oxm.jaxb.Jaxb2Marshaller的bean非常容易

http://static.springsource.org/spring-ws/site/reference/html/oxm.html (8.5.2)

记住,如果您已经有了xsd,可以使用JAXB生成Java POJO,或者如果您已经有了POJO,则可以生成xsd。如果您使用maven,可以查看以下链接:http://jaxb.java.net/maven-jaxb-schemagen/generate-mojo.html(用于代码-> xsd)和http://jaxb.java.net/jaxb-maven2-plugin/(用于xsd->代码),这可以为您节省一些工作。 - ilcavero

2
你可以使用StAX来处理这个用例。每个processBlock操作将会对XMLStreamReader进行操作并推进其状态,然后后续的processBlock操作就可以继续执行它们的任务。
package forum7011558;

import java.io.FileReader;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

public class Demo {

    public static void main(String[] args) throws Exception {
        Demo demo = new Demo();

        FileReader xml = new FileReader("src/forum7011558/input.xml");
        XMLInputFactory xif = XMLInputFactory.newFactory();
        XMLStreamReader xsr = xif.createXMLStreamReader(xml);

        demo.processBlock1(xsr);
        demo.processBlock2(xsr);
        demo.processBlock3(xsr);
        demo.processBlock4(xsr);
    }

    private void processBlock1(XMLStreamReader xsr) {
        // PROCESS BLOCK 1
    }

    private void processBlock2(XMLStreamReader xsr) {
        // PROCESS BLOCK 2
    }

    private void processBlock3(XMLStreamReader xsr) {
        // PROCESS BLOCK 3
    }

    private void processBlock4(XMLStreamReader xsr) {
        // PROCESS BLOCK 4
    }

}

这是StAX吗?它显示为JDK 1.6,我将在WebLogic 10.3上运行,所以我认为已经覆盖了? - romesub
@romesub - 是的,这个例子使用了Java SE 6(JDK 1.6)中的StAX API。WebLogic Server 10.3也支持StAX API:http://download.oracle.com/docs/cd/E12840_01/wls/docs103/upgrade/compat.html#wp1114314 - bdoughan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接