Parse XML into a DOM tree with custom object implementations in Java

I want to parse an XML document in the DOM tree in Java so that certain objects (for example, org.w3c.dom.Node or org.w3c.dom.Element instances) in the tree can be omitted from the class instances that I created, while minimizing the amount of XML-related code that I need to (re) implement. As a (very simple) example, if I have an XML element, for example:

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

I would like to configure the parser to create the following for it:

 public class Vector extends /* some parser class */ { private double[] elements; /* constructors; etc.*/ public double dotProduct(Vector v) { /* implementation */ } } 

so that I can transfer Vector instances created by the parser to, for example, the javax.xml.xpath objects' methods and work with them correctly. What is the fastest way to achieve this? Is this possible with Java SE, or are third-party libraries (such as Xerces) necessary?

+4
source share
3 answers

I'm not sure what your requirements are, but assuming you control what the XML looks like, I would use XStream . This will allow you to completely skip all the manipulations with the DOM.

Now from their two-minute tutorial , it might seem that it was not created for this use case, but in fact it is. First you create your Java classes, make sure they generate XML the way you want, and then use it to read your existing XML in your program as XStream objects. This is a very nice library to use.

+1
source

Note. I am an EclipseLink JAXB (MOXy) and a member of the JAXB Group (JSR-222) .

The JAXB Binder engine may be what you are looking for. It does not allow the use of a DOM node for a domain object, but maintains communication between the domain object and the corresponding DOM node.

Note. The following code was run when using MOXy as a JAXB provider, but it was ruled out when I used JAXB, which is part of the JDK version that I am running.

JAVA MODEL

I will use the following domain model for this example.

Customer

 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; } } 

Phone number

 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

To specify MOXy as your JAXB provider, you need to include a file named jaxb.properties in the same package as your domain model, with the following entry (see: <a2> )

 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

In the demo code below I will do the following:

  • Use XPath to search for the child, then use Binder to find the corresponding domain object.
  • Update the domain object and use Binder to apply the changes to the DOM.
  • Update the DOM and use Binder to apply the change to the domain object.

Demo

 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()); } } 

Output

 555-2OBJ 555-2DOM 
0
source

I went through this over the past 10 years, creating an XML DOM for chemistry, graphics, math, etc. My own solution was to use the DOM where the elements can be subclasses (I use xom.nu, but there are others). W3c dom does not allow subclasses (IIRC), so you will have to create a delegate model. (I tried this many years ago and rejected it, but software tools and libraries make it all a lot easier (for example, the IDE will generate delegate methods).

If you do a lot, and especially if you create many custom methods, I would recommend translating your own system. The effort will be in your methods (dotProduct), not in XML.

Here, for example, is my class for a 3D point .

 public class CMLPoint3 extends AbstractPoint3 

(which extends the base class CMLElement, which extends nu.xom.Element

Creating items is a factory. Here is a piece of my 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; 

You can see tools for copying and recursion.

The questions you need to think about are the following:

  • How closely does this relate to XSD
  • Use XSD Data Types
  • perform an input check
  • I use the DOM as the primary data structure (i)
  • how often things change.

FWIW I went through 6 revisions and consider another (using Scala as the main engine).

0
source

Source: https://habr.com/ru/post/1481282/


All Articles