使用Java中的自定义对象实现将XML解析为DOM树

时间:2013-05-17 00:26:45

标签: java xml parsing dom

我想用Java将XML文档解析为DOM树,以便树中的某些对象(例如org.w3c.dom.Nodeorg.w3c.dom.Element的实例)可以向下转换为我创建的类的实例,同时尽量减少我需要(重新)实现的XML相关代码的数量。作为一个(非常简单的)示例,如果我有一个XML元素,如:

<Vector size="5">
  1.0 -1.0 3.0 -2.73e2
</Vector>

我想自定义解析器以实例化以下内容:

public class Vector extends /* some parser class */ {
  private double[] elements;

  /* constructors; etc.*/

  public double dotProduct(Vector v) {
    /* implementation */
  }
}

这样我就可以将解析器创建的Vector实例传递给,例如javax.xml.xpath个对象的方法,并让它们正常工作。实现这一目标的最快方法是什么?是否可以单独使用Java SE,还是第三方库(例如Xerces)?

3 个答案:

答案 0 :(得分:1)

我不确定您的要求是什么,但假设您掌握了XML的样子,我会使用的是XStream。它将允许您完全跳过所有DOM操作。

现在来自their 2 minute tutorial,它似乎不是为这个用例而构建的,但它实际上是。首先创建java类,确保它们以您希望的方式生成XML,然后使用它将已存在的XML作为XStream对象读回到程序中。这是一个非常令人愉快的图书馆。

答案 1 :(得分:0)

注意:我是EclipseLink JAXB (MOXy)主管,是JAXB (JSR-222)专家组的成员。

JAXB中的Binder机制可能正是您所寻找的。它不允许将DOM节点强制转换为域对象,但它确实维护域对象与其对应的DOM节点之间的链接。

注意:当使用MOXy作为JAXB提供程序时,以下代码运行干净,但在使用JDK版本中包含的JAXB的impl时,我发生了异常,我正在运行。< / p>

JAVA模型

我将在此示例中使用以下域模型。

<强>客户

import java.util.*;
import javax.xml.bind.annotation.*;

@XmlRootElement
public class Customer {

    private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();

    @XmlElementWrapper
    @XmlElement(name="phoneNumber")
    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

}

<强> ******中国

import javax.xml.bind.annotation.*;

public class PhoneNumber {

    private String type;
    private String number;

    @XmlAttribute
    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @XmlValue
    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

}

jaxb.properties

要将MOXy指定为JAXB提供程序,您需要在与域模型相同的包中包含名为jaxb.properties的文件,并带有以下条目(请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

XML(input.xml)

<?xml version="1.0" encoding="UTF-8"?>
<customer>
    <phoneNumbers>
        <phoneNumber type="work">555-1111</phoneNumber>
        <phoneNumber type="home">555-2222</phoneNumber>
    </phoneNumbers>
</customer>

DEMO CODE

在下面的演示代码中,我将执行以下操作:

  1. 使用XPath查找子元素,然后使用Binder查找相应的域对象。
  2. 更新域对象并使用Binder将更改应用于DOM。
  3. 更新DOM并使用Binder将更改应用于域对象。
  4. <强>演示

    import javax.xml.bind.Binder;
    import javax.xml.bind.JAXBContext;
    import javax.xml.parsers.*;
    import javax.xml.xpath.*;
    
    import org.w3c.dom.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.parse("src/forum16599580/input.xml");
    
            XPathFactory xpf = XPathFactory.newInstance();
            XPath xpath = xpf.newXPath();
    
            JAXBContext jc = JAXBContext.newInstance(Customer.class);
            Binder<Node> binder = jc.createBinder();
            binder.unmarshal(document);
    
            // Use Node to Get Object
            Node phoneNumberElement = (Node) xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.NODE);
            PhoneNumber phoneNumber = (PhoneNumber) binder.getJAXBNode(phoneNumberElement);
    
            // Modify Object to Update DOM
            phoneNumber.setNumber("555-2OBJ");
            binder.updateXML(phoneNumber);
            System.out.println(xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.STRING));
    
            // Modify DOM to Update Object
            phoneNumberElement.setTextContent("555-2DOM");
            binder.updateJAXB(phoneNumberElement);
            System.out.println(phoneNumber.getNumber());
        }
    
    }
    

    <强>输出

    555-2OBJ
    555-2DOM
    

答案 2 :(得分:0)

在过去的10年里,我一直在为化学,图形,数学等构建XML DOM。我自己的解决方案是使用DOM,其中元素可以被子类化(我使用xom.nu但是那里是其他人)。 w3c dom不允许子类化(IIRC),因此您必须构建委托模型。 (我多年前尝试过并拒绝它,但软件工具和库使这一切变得更加容易(例如,IDE将生成委托方法)。

如果您正在做很多事情,特别是如果您要创建大量自定义方法,那么我建议您自己编辑系统。努力将在您的方法(dotProduct),而不是XML。

例如,is my class for a 3D point

public class CMLPoint3 extends AbstractPoint3 

(扩展了基类CMLElement,扩展了nu.xom.Element

元素的创建是工厂。这是我的SVGDOM的一大块:

public static SVGElement readAndCreateSVG(Element element) {
    SVGElement newElement = null;
    String tag = element.getLocalName();
    if (tag == null || tag.equals(S_EMPTY)) {
        throw new RuntimeException("no tag");
    } else if (tag.equals(SVGCircle.TAG)) {
        newElement = new SVGCircle();
    } else if (tag.equals(SVGClipPath.TAG)) {
        newElement = new SVGClipPath();
    } else if (tag.equals(SVGDefs.TAG)) {
        newElement = new SVGDefs();
    } else if (tag.equals(SVGDesc.TAG)) {
        newElement = new SVGDesc();
    } else if (tag.equals(SVGEllipse.TAG)) {
        newElement = new SVGEllipse();
    } else if (tag.equals(SVGG.TAG)) {

...
    } else {
            newElement = new SVGG();
            newElement.setClassName(tag);
            System.err.println("unsupported svg element: "+tag);
        }
        if (newElement != null) {
            newElement.copyAttributesFrom(element);
            createSubclassedChildren(element, newElement);
        }
        return newElement;

您可以看到用于复制和递归的工具。

您需要考虑的问题是:

  • 这与XSD有多接近
  • 我是否使用XSD数据类型
  • 我对输入进行验证
  • 我使用DOM作为主要数据结构(我这样做)
  • 事情的变化频率。

FWIW我已经对此进行了6次修改,并正在考虑另一个(使用Scala作为主要引擎)。