使用XSLT样式表与使用DOM解析器手动解析XML文件相比,有哪些优势?

4

对于我们的一个应用程序,我编写了一个实用程序,它使用Java的DOM解析器。它基本上接收一个XML文件,解析它,然后使用以下方法之一来获取数据并处理数据。

getElementByTagName()
getElementAtIndex()
getFirstChild()
getNextSibling()
getTextContent()

现在我需要做同样的事情,但我想知道是否使用XSLT样式表会更好。发送给我们XML文件的组织不断更改其模式,这意味着我们必须更改我们的代码以适应这些模式更改。我不太熟悉XSLT过程,因此我正在尝试找出是否最好使用XSLT样式表而不是“手动解析”。
XSLT样式表看起来很有吸引力的原因是,如果XML文件的模式更改,我认为我只需要更改样式表?这正确吗?
我想知道两者中哪个(XSLT转换器或DOM解析器)在性能方面更好。对于手动选项,我只是使用DOM解析器解析xml文件。 XSLT转换器如何解析文件?与手动解析xml文件相比,它是否包括额外的开销?我问的原因是性能很重要,因为我将处理的数据的性质。
有什么建议吗?
谢谢
编辑
基本上,我当前正在做的是解析XML文件并处理一些xml元素中的值。我不会将xml文件转换为任何其他格式。我只是提取一些值,从Oracle数据库中提取一行,并将新行保存到不同的表中。我解析的xml文件只包含我用于从数据库检索某些数据的参考值。
在这种情况下,xslt不适用吗?是否有更好的方法可以使用以避免如果模式更改而进行代码更改?
编辑2
对于我从XML数据中提取的内容,抱歉没有表达得足够清楚。基本上,有一个包含一些信息的XML文件。我从XML文件中提取此信息并使用它来从本地数据库检索更多信息。 xml文件中的数据更像是我需要在数据库中获取的参考键。然后,我将从XML文件中提取的内容加上使用XML文件中的特定键检索到的内容,并将该数据保存到另一个数据库表中。
我遇到的问题是我知道如何编写DOM解析器以从XML文件中提取所需的信息,但我想知道是否使用XSLT样式表是更好的选择,因为如果模式更改,我就不必更改代码。
根据下面的回复,听起来XSLT仅用于将XML文件转换为另一个XML文件或其他格式。鉴于我不打算转换XML文件,因此可能没有必要增加解析XSLT样式表以及XML文件的额外开销。

XSLT用于将XML文档转换为另一个(XML / HTML / text)文档。它不用于解析和访问文档内容。你的DOM解析器是做什么的? - JB Nizet
2
我认为要避免主观问题,需更好地描述需求。小提示:使用低级别的DOM方法遍历(而非解析)树可能比高级语言(如XSLT)更快;设计和更新低级别的遍历可能比高级语言(如XSLT)更难且复杂。如果在处理传入数据后,需要构建另一个XML树,则再次使用低级别的方法可能更快,但维护和更新会更困难。此外,我们将涉及特定的XSLT领域... - user357812
@Alejandro +1。你应该真的把这个发表为一个答案。 - Flack
4个回答

4
将XML文档转换为其他格式是XSLT存在的原因。您可以使用XSLT输出HTML、JSON、另一个XML文档或任何您需要的内容。您不需要指定想要的输出类型。如果您只是获取一些元素的内容,那么也许您不需要使用XSLT。但对于任何更多的需求,XSLT提供了一种优雅的解决方案。这主要是因为XSLT了解它正在处理的文档的结构。它的处理模型是树遍历和模式匹配,这本质上就是您在Java中手动执行的操作。
您可以使用XSLT将源数据转换为所需的表示形式。您的代码将始终在此结构上运行。然后,当您正在使用的组织更改模式时,您只需更改XSLT以将新的XML转换为自定义格式即可。您的其他代码都不需要更改。为什么您的业务逻辑应该关心其源数据的格式呢?

1
使用XSLT的另一个原因是将代码与实际文件格式解耦,当您无法控制格式时,这非常方便(通常情况下如此)。 - biziclop

3
你说得对,XSLT的处理模型基于基于规则的事件驱动方法使你的代码更具有适应性来应对架构方面的变化。
因为这是一种与DOM使用的过程/导航方法不同的处理模型,所以需要学习和熟悉,有些人可能会感到沮丧。如果你想走这条路,请耐心等待,因为在理解这些思想之前需要一段时间。一旦掌握了它,比DOM编程容易得多。
一个好的XSLT处理器的性能足够满足你的需求。当然,像任何语言一样,也可能编写非常低效的代码,但我很少见到XSLT是瓶颈的系统。很多时候,XML解析所花费的时间比XSLT处理还要长(这与DOM或JAXB或其他任何东西的成本相同)。
正如其他人所说,很多取决于你想要做什么与XML数据,而你并没有真正解释清楚。

1

我认为你需要的实际上是XPath表达式。你可以在某个属性文件或者你用来检索设置参数的任何地方配置该表达式。

这样,当你的客户将你使用的信息隐藏在另一个地方时,你只需更改XPath表达式。

基本上,XSLT是过度设计了,你只需要一个XPath表达式。一个单一的XPath表达式将允许你定位到每个你想要的值。

更新

由于我们现在谈论的是JDK 1.4,因此我在下面列出了三种使用XPath从XML文件中获取文本的不同方法。(尽可能简单,抱歉没有NPE保护)

从最新的开始。

0. 首先是示例XML配置文件

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <param id="MaxThread" desc="MaxThread"        type="int">250</param>
    <param id="rTmo"      desc="RespTimeout (ms)" type="int">5000</param>
</config>

1. 使用 Java SE 5.0 的 JAXP 1.3 标准部分

import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.Document;

public class TestXPath {

    private static final String CFG_FILE = "test.xml" ;
    private static final String XPATH_FOR_PRM_MaxThread = "/config/param[@id='MaxThread']/text()";
    public static void main(String[] args) {

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        docFactory.setNamespaceAware(true);
        DocumentBuilder builder;
        try {
            builder = docFactory.newDocumentBuilder();
            Document doc = builder.parse(CFG_FILE);
            XPathExpression expr = XPathFactory.newInstance().newXPath().compile(XPATH_FOR_PRM_MaxThread);
            Object result = expr.evaluate(doc, XPathConstants.NUMBER);
            if ( result instanceof Double ) {
                System.out.println( ((Double)result).intValue() );
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 使用 Java SE 1.4-2 的 JAXP 1.2 标准部分

import javax.xml.parsers.*;
import org.apache.xpath.XPathAPI;
import org.w3c.dom.*;

public class TestXPath {

    private static final String CFG_FILE = "test.xml" ;
    private static final String XPATH_FOR_PRM_MaxThread = "/config/param[@id='MaxThread']/text()";

    public static void main(String[] args) {

        try {
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            docFactory.setNamespaceAware(true);
            DocumentBuilder builder = docFactory.newDocumentBuilder();
            Document doc = builder.parse(CFG_FILE);
            Node param = XPathAPI.selectSingleNode( doc, XPATH_FOR_PRM_MaxThread );
            if ( param instanceof Text ) {
                System.out.println( Integer.decode(((Text)(param)).getNodeValue() ) ); 
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3. 使用 Java SE 1.4 + jdom + jaxen 的 JAXP 1.1 标准

您需要添加这两个 jar 包(可从 www.jdom.org 获取 - 二进制文件,jaxen 已包含在内)。

import java.io.File;
import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;

public class TestXPath {

    private static final String CFG_FILE = "test.xml" ;
    private static final String XPATH_FOR_PRM_MaxThread = "/config/param[@id='MaxThread']/text()";

    public static void main(String[] args) {
        try {
            SAXBuilder sxb = new SAXBuilder();
            Document doc = sxb.build(new File(CFG_FILE));
            Element root = doc.getRootElement();
            XPath xpath = XPath.newInstance(XPATH_FOR_PRM_MaxThread);
            Text param = (Text) xpath.selectSingleNode(root);
            Integer maxThread = Integer.decode( param.getText() );
            System.out.println( maxThread );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这不仅仅是“过度杀伤”,而且是错误的工具,因为XSLT是用于直接创建输出的。 - Jesse Millikan
1
@Jesse,99%正确!但是我保持开放,因为可以将ByteArrayOutputStream用作转换输出。这样,您可以在字符串中获取结果。这可能是Ziggy的意图。当我想要优化样式表并通过自定义扩展替换耗时的模板时,我经常使用此技术。当您处于预生产状态时,您会将比较器与大量代表性输入一起使用,并将扩展的结果与遗留所有这些字符串的结果进行比较。 - Alain Pannetier
@Alain Pannetier:+1 我同意现在问题已经澄清:遍历输入源不需要中间格式,而XPath始终比低级DOM方法更灵活。 - user357812
@Alain,我按照你的建议尝试了上述的2和3选项。 对于第二个选项,我无法导入org.apache.xpath.XPathAPI包。它出现了错误“访问限制:由于C:\ Java \ j2sdk1.4.1_07 \ jre \ lib \ rt.jar所需的库的限制,XPathAPI类型不可访问”。我在谷歌上搜索了一下,似乎我必须玩弄JVM中的库才能使其正常工作。我决定选择第三个选项,这个选项完美地解决了问题。我将xpath表达式放到属性文件中,并使用一个通用的xml实用程序类,该类被所有解析器使用。 - ziggy
选项2适用于1.4-2版本,而您似乎使用的是1.4.1_07版本。这就解释了为什么它不起作用。我以为您可能在使用1.4-2版本,但也为您提供了选项3以防万一。事后看来,那对您来说是最好的选择...恭喜您。 - Alain Pannetier
显示剩余2条评论

0

由于性能很重要,我建议使用SAX解析器。JAXB将为您提供与DOM解析大致相同的性能,而且更易于维护。如果您正在使用JAXB,则对模式进行更改也不应该对您产生负面影响,只需获取新模式并重新生成类即可。如果在JAXB和域逻辑之间建立了桥梁,则可以在该层中吸收变化,而无需担心XML。我更喜欢将XML视为仅用于消息传递层的消息。所有应用程序代码都应该对XML模式保持不可知状态。


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