5

我想将 XML 文档解析为 Java 中的 DOM 树,以便树中的某些对象(例如org.w3c.dom.Nodeor的实例org.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)?

4

3 回答 3

1

我不确定您的要求是什么,但假设您可以控制 XML 的外观,我会使用XStream。它将允许您完全跳过所有 DOM 操作。

现在从他们的 2 分钟教程来看,它可能看起来不是为这个用例而构建的,但实际上是。您首先创建您的 java 类,确保它们以您希望的方式生成 XML,然后使用它将您已经存在的 XML 作为 XStream 对象读回您的程序。这是一个使用起来非常愉快的图书馆。

于 2013-05-17T00:29:53.430 回答
0

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

如果你做了很多,特别是如果你正在创建很多自定义方法,那么我建议你滚动你自己的系统。工作将在您的方法(dotProduct)中,而不是在 XML 中。

例如,这里是我的 3D 点类

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 作为主要引擎)。

于 2013-05-17T08:53:59.623 回答
0

注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB (JSR-222)专家组的成员。

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

注意: 以下代码在使用 MOXy 作为 JAXB 提供程序时运行良好,但在使用我碰巧正在运行的 JDK 版本中包含的 JAXB 的 impl 时抛出异常。

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 -你的.html )

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

XML (输入.xml)

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

演示代码

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

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

演示

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
于 2013-05-17T03:06:30.773 回答