使用Java中的自定义对象实现将XML解析到DOM树中
我想将XML文档解析为Java中的DOM树,使树中的某些对象(例如org.w3c.dom.Node
或org.w3c.dom.Element
的实例)可以向下转换为类我已经创建了,同时尽量减少了需要(重新)实现的与XML相关的代码量。作为(非常简单的)例如,如果我有一个像XML元素:使用Java中的自定义对象实现将XML解析到DOM树中
<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)是必要的?
我不确定你的要求是什么,但假设你在控制XML的外观,我会用的是XStream。它将允许你完全跳过所有的DOM操作。
现在从their 2 minute tutorial,它可能看起来不像它是为这个用例而构建的,但它实际上是。首先创建您的java类,确保它们以您希望的方式生成XML,然后使用它将您已经存在的XML作为XStream对象读回到程序中。这是一个非常愉快的图书馆使用。
我需要对我的对象(即其成员)所做的更改立即反映在DOM中。看来XStream从DOM实例化了一组单独的对象。 – TechnocratiK
为什么你需要一个DOM? –
主要使用XPath来导航XML层次结构。 – TechnocratiK
备注:我是EclipseLink JAXB (MOXy)的领导和JAXB (JSR-222)专家组的成员。
JAXB中的Binder
机制可能是您正在寻找的。它不允许将DOM节点转换为域对象,但它确实保持域对象与其对应的DOM节点之间的链接。
注:使用莫西作为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
要指定莫西为您的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(输入。XML)
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<phoneNumbers>
<phoneNumber type="work">555-1111</phoneNumber>
<phoneNumber type="home">555-2222</phoneNumber>
</phoneNumbers>
</customer>
演示代码
在演示代码下面我将做到以下几点:
- 使用XPath找到一个子元素,然后使用
Binder
找到相应的域对象。 - 更新域对象并使用
Binder
将更改应用到DOM。 - 更新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
我经历过的正是这种在过去的10年里,建设XML DOM的用于化学,图形,数学等。我自己的解决方案是使用一个DOM,其中的元素可以被分类(我使用xom.nu,但也有其他的)。 w3c dom不允许子类化(IIRC),所以你将不得不建立一个委托模型。 (我在很多年以前就尝试过并拒绝它,但是软件工具和库使这一切变得更加容易(例如,IDE将生成委托方法)
如果您正在做很多事情,尤其是如果您要创建很多定制的方法,那么我会推荐滚动你自己的系统。这一努力将在你的方法(dotProduct),而不是XML。
这里,例如,is my class for a 3D point。
public class CMLPoint3 extends AbstractPoint3
(延伸基地class 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作为主引擎)。
您是否掌握了XML的外观? –
是的,我完全控制了XML及其模式。 – TechnocratiK