Java JAXB marshall into DOM Document - java

I have JAXB annotations:
#XmlElement(name="String")
private String string = "one";
#XmlElement(name="ArrayOne")
private ArrayList<String> array1 = new ArrayList<String>();
and marshalling:
array.add("Just one");
JAXBContext jc1 = JAXBContext.newInstance( getClass() );
Marshaller marshaller = jc1.createMarshaller();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance() ;
Document doc = factory.newDocumentBuilder().newDocument();
marshaller.marshal(this, doc);
If I dig into document nodes, I cannot see any difference between
node elements.
My question is, if there is any trick how to marshall into DOM document,
that node elements will be somehow distinct, whether is simple object(String),
or array object. Marshaller of course is aware of field types, so I wonder,
if it puts some flag in Element data.
DOM structure is
NodeName:String NodeContent:one
NodeName:ArrayOne NodeContent:Just one
but I would like to have like:
NodeName:String NodeContent:one
NodeName:ArrayOne
Children:
NodeName:ArrayOne NodeContent:Just one
so I know, ArrayOne is array, no matter of just one object.
Note, that I cannot change annotations, since there is not always source available.

You can create wrapper element for collections using #XmlElementWrapper:
#XmlElement(name="String")
private String string = "one";
#XmlElementWrapper(name="ArrayOne")
private ArrayList<String> array1 = new ArrayList<String>();
XML output for this mapping looks like this:
<testElement>
<String>one</String>
<ArrayOne>
<array1>one</array1>
</ArrayOne>
</testElement>
Update for comment:
Adding wrapper on DOM Document manually (probably there is an easier way, maybe by using a Transformer):
TestElement te = new TestElement();
JAXBContext jc1 = JAXBContext.newInstance(TestElement.class);
Marshaller marshaller = jc1.createMarshaller();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance() ;
Document doc = factory.newDocumentBuilder().newDocument();
marshaller.marshal(te, doc);
NodeList nodeList = doc.getDocumentElement().getChildNodes();
Node newNode = doc.createElement("ArrayOneWrapper");
List<Node> arrayOneElements = new ArrayList<>();
for (int i = 0; i < nodeList.getLength(); i++) {
Node n = nodeList.item(i);
if (n.getNodeName().equals("ArrayOne")) {
arrayOneElements.add(n);
}
}
for (Node n : arrayOneElements) {
newNode.appendChild(n);
}
XML output:
<testElement>
<String>one</String>
<ArrayOneWrapper>
<ArrayOne>one</ArrayOne>
<ArrayOne>two</ArrayOne>
</ArrayOneWrapper>
</testElement>

Related

Retrieving child Node value based on Parent Node attribute using DOM parser

I have XML content as below
<PARENT1 ATTR="FILE1">
<TITLE>test1.pdf</TITLE>
</PARENT1>
<PARENT2 ATTR="FILE2">
<TITLE>test2.pdf</TITLE>
</PARENT2>
I want to create a hashmap in Java by adding map Key as Parent attribute value and map Value as Child Node Value.
Example:
map.put("FILE1","test1.pdf");
map.put("FILE2","test2.pdf");
I know to get all child nodes list, but i am not getting how to get child node value based on parent node attribute or parent node.
How to achieve this in Java using DOM or SAX parser.
Any help is greatly appreciated.
Regards,
Tendulkar
If the XML files aren't huge, I'd recommend using JDOM instead of the default DOM parser as it's much more user friendly.
Here's an example to kinda do what you want, but you'll need to do the error checking and such yourself.
public class XmlParser {
private static final String xml = "<parents><parent name=\"name1\"><title>title1</title></parent><parent name=\"name2\"><title>title2</title></parent></parents>";
public static final void main(String [] args) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xml)));
Node parents = doc.getChildNodes().item(0);
Map<String, String> dataMap = new HashMap<>();
for (int i = 0; i < parents.getChildNodes().getLength(); i++) {
Node parent = parents.getChildNodes().item(i);
String name = parent.getAttributes().getNamedItem("name").getNodeValue();
String title = parent.getChildNodes().item(0).getTextContent();
dataMap.put(name, title);
}
System.out.println(dataMap);
}
}

Getting Element (not Node) below root in Java DOM (XML parser)

I need to get the tag of an element right below the root, but DOM seems only to offer methods getting child nodes (not elements) and you cant cast from one to the other.
http://ideone.com/SUjRmn
#Override
public void loadXml(String filepath) throws Exception {
File f = new File(filepath);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
Document doc = null;
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
try {
doc = db.parse(f);
} catch (SAXException | IOException | NullPointerException e) {
e.printStackTrace();
}
Element root = doc.getDocumentElement();
Node firstChild = root.getFirstChild();
String tag = firstChild.getNodeName();
//here is the problem. I can't cast from Node to Element and Node
//stores only an int value, not the name of the object I want to restore
ShapeDrawer drawable = null;
switch (tag) {
case "scribble":
drawable = new ScribbleDrawer();
...
From the class to restore:
#Override
public void setValues(Element root) {
NodeList nodelist = null;
nodelist = root.getElementsByTagName("color");
colorManager.setColor((nodelist.item(0).getTextContent()));
this.color = colorManager.getCurrentColor();
System.out.println(color.toString());
nodelist = root.getElementsByTagName("pressx");
pressx = Integer.parseInt(nodelist.item(0).getTextContent());
System.out.println(pressx);
nodelist = root.getElementsByTagName("pressy");
pressy = Integer.parseInt(nodelist.item(0).getTextContent());
System.out.println(pressy);
nodelist = root.getElementsByTagName("lastx");
lastx = Integer.parseInt(nodelist.item(0).getTextContent());
nodelist = root.getElementsByTagName("lasty");
lasty = Integer.parseInt(nodelist.item(0).getTextContent());
}
public void toDOM(Document doc, Element root) {
System.out.println("ScribbleDrawer being saved");
Element shapeBranch = doc.createElement("scribble");
Attr attr1 = doc.createAttribute("hashcode");
attr1.setValue(((Integer) this.hashCode()).toString());
shapeBranch.setAttributeNode(attr1);
root.appendChild(shapeBranch);
Element eColor = doc.createElement("color");
eColor.setTextContent(colorManager.namedColorToString(color));
shapeBranch.appendChild(eColor);
// creating tree branch
Element press = doc.createElement("press");
Attr attr2 = doc.createAttribute("pressx");
attr2.setValue(((Integer) pressy).toString());
press.setAttributeNode(attr2);
Attr attr3 = doc.createAttribute("pressy");
attr3.setValue(((Integer) pressy).toString());
press.setAttributeNode(attr3);
shapeBranch.appendChild(press);
Element last = doc.createElement("last");
Attr attr4 = doc.createAttribute("lastx");
attr4.setValue(((Integer) lastx).toString());
last.setAttributeNode(attr4);
Attr attr5 = doc.createAttribute("lasty");
attr5.setValue(((Integer) lasty).toString());
last.setAttributeNode(attr5);
shapeBranch.appendChild(last);
}
I know other parsers are easier, but I am almost finished and when it comes to polymorphy JAXB seems to be just as complicated with Option-marshalling etc
EDIT: this is what the xml looks like; instead of "scribble" other tags/polymorphic children are possible which are deserialized from different instance variables (and thus different DOM-trees except for the root)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Drawables>
<scribble hashcode="189680059">
<color>Black</color>
<press pressx="221" pressy="221"/>
<last lastx="368" lasty="219"/>
</scribble>
<scribble hashcode="1215837841">
<color>Black</color>
<press pressx="246" pressy="246"/>
<last lastx="368" lasty="221"/>
</scribble>
If your node is an Element, you can cast it from node to element. But your first child might also be a text node, which can't be cast, of course. You have to test the nodes for their NodeType before casting.
If your XML is not using namespaces, you can use a method like this one to extract your child elements. It receives a list of nodes, test each one and returns a list containing only the elements:
public static List getChildren(Element element) {
List<Element> elements = new ArrayList<>();
NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
elements.add((Element) node);
}
}
return elements;
}
An alternative is to use an API which already includes such utility methods, like DOM4J, or JDOM.

Reading a complex XML Java

Thanks for considering this question.
I'm reading a complex XML file, which as you can see in the code has 44 main "nodes". Each node has further nested elements and so on.
I have managed to read the info from the first node but it seems that after the first iteration, returns only null. What could I be missing?
for (int i=0; i<nodeList.getLength(); i++){
log(String.valueOf(i));
Element flightInfo = (Element)nodeList.item(i);
NodeList flights = flightInfo.getElementsByTagName("flight");
Element flight = (Element)flights.item(0);
String flightId = flight.getAttribute("id");
String airlineCode = flight.getAttribute("airlineCode");
String operationType = flight.getAttribute("operationType");
String flightRoute= flight.getAttribute("flightType");
String scheduledTime = flight.getAttribute("scheduledTime");
NodeList routingList = flight.getElementsByTagName("routingList");
Element iatas = (Element)routingList.item(0);
NodeList _iata = (iatas.getElementsByTagName("IATA"));
String iata = _iata.item(i).getFirstChild().getNodeValue();
NodeList times = flight.getElementsByTagName("times");
Element realTimes = (Element)times.item(0);
NodeList _realTime = (realTimes.getElementsByTagName("realTime"));
String realTime = _realTime.item(0).getFirstChild().getNodeValue();
NodeList means = flight.getElementsByTagName("means");
Element gates = (Element)means.item(0);
NodeList _gate = gates.getElementsByTagName("gate");
Element gate = (Element)_gate.item(0);
String gateId = gate.getAttribute("id");
Element bagClaimList = (Element)means.item(0);
NodeList bagClaims = bagClaimList.getElementsByTagName("bagClaim");
Element bagClaim = (Element)bagClaims.item(0);
String bagId = bagClaim.getAttribute("id");
Element standList = (Element)means.item(0);
NodeList stands = standList.getElementsByTagName("stand");
Element _stand = (Element)stands.item(i);
String standId = _stand.getAttribute("id");
NodeList remarks = flight.getElementsByTagName("flight");
Element remarkCodes = (Element)remarks.item(0);
NodeList _remarkCode = (remarkCodes.getElementsByTagName("remarkCode"));
String remarkCode = _remarkCode.item(0).getFirstChild().getNodeValue();
flightList.add(new Flight(flightId, airlineCode, operationType,iata, scheduledTime, iata, realTime, gateId, bagId, standId, remarkCode));
log("Added new flightInfo");
}
The XML I'm reading is the following:
<flightData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="file://c:/SITA/IKUSI FIDS/FIDS.XSD">
<flightInfo>
<flight id="AM2613" airlineCode="AM" flightNumber="2613" operationType="D" flightType="D" scheduledTime="2013-07-18T07:00:00">
<routingList>
<IATA>MTY</IATA>
</routingList>
<times>
<realTime>2013-07-18T07:00:00</realTime>
</times>
<means>
<gateList>
<gate id="N/14"/>
</gateList>
<bagClaimList>
<bagClaim id="2"/>
</bagClaimList>
<standList>
<stand id="5"/>
</standList>
</means>
<remarks>
<remarkCode>DEP</remarkCode>
</remarks>
</flight>
</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
<flightInfo>...</flightInfo>
You'd be better off using JAXB: with the xsd file you will be able to generate java classes representing your model and won't have to write all this data extraction code.

Parsing xml string containing hyperlink

I am using DOM to parse an XML string as in the following example. This works great except in one instance. The document which I am trying to parse looks like this:
<response requestID=\"1234\">
<expectedValue>Alarm</expectedValue>
<recommendations>For steps on how to resolve visit Website and use the search features for \"Alarm\"<recommendations>
<setting>Active</setting>
<response>
The code I used to parse the XML is as follows:
try {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmlResult));
Document doc = db.parse(is);
NodeList nlResponse = doc.getElementsByTagName("response");
String[] String = new String[3]; //result entries
for (int i = 0; i < nlResponse.getLength(); i++) {
Element e = (Element) nlResponse.item(i);
int c1 = 0; //count for string array
NodeList ev = e.getElementsByTagName("expectedValue");
Element line = (Element) ev.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
NodeList rec = e.getElementsByTagName("recommendations");
line = (Element) rec.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
NodeList set = e.getElementsByTagName("settings");
line = (Element) set.item(0);
String[c1] = (getCharacterDataFromElement(line));
c1++;
I am able to parse the code and put the result into a string array (as opposed to the System.out.println()). With the current code, my string array looks as follows:
String[0] = "Alarm"
String[1] = "For steps on how to resolve visit"
String[2] = "Active"
I would like some way of being able to read the rest of the information within "Recommendations" in order to ultimately display the hyperlink (along with other output) in a TextView. How can I do this?
I apologize for my previous answer in assuming your xml was ill-formed.
I think what is happening is that your call to the getCharacterDataFromElement is only looking at the first child node for text, when it will need to look at all the child nodes and getting the href attribute as well as the text for the 2nd child node when looking at the recommendations node.
e.g. after getting the Element for recommendation
String srec = "";
NodeList nl = line.getChildNodes();
srec += nl.item(0).getTextContent();
Node n = nl.item(1);
NamedNodeMap nm = n.getAttributes();
srec += "" + n.getTextContent() + "";
srec += nl.item(2).getTextContent();
String[c1] = srec;

How to remove elements of a page in htmlunit

Normally in PHP, I would just parse the old document and write to the new document while ignoring the unwanted elements.
This was the first solution I came up with:
DocumentBuilder builder = DocumentBuilderFactory
.newInstance()
.newDocumentBuilder();
StringReader reader = new StringReader( xml );
Document document = builder.parse( new InputSource(reader) );
XPathExpression expr = XPathFactory
.newInstance()
.newXPath()
.compile( ... );
Object result = expr.evaluate(document, XPathConstants.NODESET);
Element el = document.getDocumentElement();
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
el.removeChild( nodes.item(i) );
}
As you can see it's kinda long. Being a coder who strives for simplicity, I decided to take Ahmed's advice hoping I'll find a better solution and I came up with this:
List<?> elements = page.getByXPath( ... );
DomNode node = null;
for( Object o : elements ) {
node = (DomNode)o;
node.getParentNode().removeChild( node );
}
Please note these are just snippets, I omitted the imports and the XPath expressions but you get the idea.
Have a look at the DOM methods, you can remove nodes.
http://htmlunit.sourceforge.net/apidocs/com/gargoylesoftware/htmlunit/html/DomNode.html

Categories