I have an XML file containing a bunch of simulation settings (partial example below). I would like to load these settings into a Java class, so that the settings are available later without having to write cumbersome DOM/XPath jargon (and import the associated packages) every time I (or another programmer who isn't fluent in DOM/XPath) want to access a specific setting.
Right now I set up a number of sub-classes that represent each level of information in the XML tree, and "manually" parse out the info to all these sub-classes. The result is, for example, that if I want to get Direction number 3, I can write:
settings.setup.directions[3]
I guess this works ok, but it sure feels rigid.
Is there a smarter way of doing this? Should we just stick to the DOM and skip this conversion business? (Please no!)
Note that I am not looking for instructions on how to load an XML file -- I know how to load it into a DOM document and parse it with XPath.
<?xml version="1.0"?>
<Settings>
<Identity>
<JobNumber>1234567</JobNumber>
<SimulationName>MyTest</SimulationName>
</Identity>
<PreProcessing >
<Tolerance>0.01</Tolerance>
</PreProcessing >
<PreprocessedInputData>
<PreChewedThing></PreChewedThing>
<OtherThing></OtherThing>
</PreprocessedInputData>
<Setup>
<DomainExtent>
<XMin>260</XMin>
<XMax>290</XMax>
<YMin>523</YMin>
<YMax>565</YMax>
</DomainExtent>
<Directions>
<Direction Index = "1">0</Direction>
<Direction Index = "2">10</Direction>
<Direction Index = "3">20</Direction>
<Direction Index = "4">30</Direction>
</Directions>
</Setup>
</Settings>
You can use JAXB for this purpose, it is meant to bind XML to Java classes.
There is a useful guide on http://jaxb.java.net/guide/ and a tutorial on http://jaxb.java.net/tutorial/
If you have flexibility over the layout of the XML file, and don't especially need to use a settings class with JAXB, try Commons Configuration:
Creating an XML settings file is as easy as:
XMLConfiguration configCreate = new XMLConfiguration();
configCreate.setFileName("settings.xml");
configCreate.addProperty("somesetting", "somevalue");
configCreate.save();
Reading from the XML settings file:
XMLConfiguration configRead = new XMLConfiguration("settings.xml");
String settingValue = configRead.getString("somesetting");
See http://commons.apache.org/configuration/
In my opinion, the best and simpliest way is using Java and XPath. This is an example:
<settings>
<type>jdbc-mysql</tipus>
<usr>usr</usr>
<pass>pass</pass>
<url>jdbc:mysql://192.168.1.123:3306/notifications_db</url>
<schema>notificacions_db</schema>
<date_format>yyyy-MM-dd HH:mm:ss</date_format>
<prefix_package>false</prefix_package>
<use_ssl>false</use_ssl>
<auto_reconnect>true</auto_reconnect></settings>
Java main class Example:
public static void main(String[] args) {
XPath xpath = XPathFactory.newInstance().newXPath();
String xpathExpression = "/settings";
InputSource inputSource = new InputSource("basedao-settings.xml");
try {
NodeList lstRoot = (NodeList) xpath.compile(xpathExpression).evaluate(inputSource, XPathConstants.NODESET);
NodeList lstChilds = lstRoot.item(0).getChildNodes();
for (int i = 0; i < lstChilds.getLength(); i++) {
System.out.println(lstChilds.item(i).getLocalName());
System.out.println(lstChilds.item(i).getTextContent());
}
} catch (XPathExpressionException e) {
e.printStackTrace();
}
}
Related
I have this xml file:
<?xml version="1.0" encoding="UTF-8"?>
<iet:aw-data xmlns:iet="http://care.aw.com/IET/2007/12" class="com.aw.care.bean.resource.MessageResource">
<iet:metadata filter=""/>
<iet:message-resource>
<iet:message>some message 1</iet:message>
<iet:customer id="1"/>
<iet:code>edi.claimfilingindicator.11</iet:code>
<iet:locale>iw_IL</iet:locale>
</iet:message-resource>
<iet:message-resource>
<iet:message>some message 2</iet:message>
<iet:customer id="1"/>
<iet:code>edi.claimfilingindicator.12</iet:code>
<iet:locale>iw_IL</iet:locale>
</iet:message-resource>
.
.
.
.
</iet:aw-data>
Using this code below i'm getting over the data and finding what I need.
try {
FileInputStream fileIS = new FileInputStream(new File("resources\\bootstrap\\content\\MessageResources_iw_IL\\MessageResource_iw_IL.ctdata.xml"));
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String query = "//*[local-name()='message-resource']//*[local-name()='code'][contains(text(), 'account')]";
NodeList nodeList = (NodeList) xPath.compile(query).evaluate(xmlDocument, XPathConstants.NODESET);
System.out.println("size= " + nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getNodeValue());
}
}
catch (Exception e){
e.printStackTrace();
}
The issue is that i'm getting only null values while printing in the for loop, any idea why it's happened?
The code needs to return a list of nodes which have a code and message fields that contains a given parameters (same as like SQL query with two parameters with operator of AND between them)
Check the documentation:
https://docs.oracle.com/javase/7/docs/api/org/w3c/dom/Node.html
getNodeValue() applied to an element node returns null.
Use getTextContent().
Alternatively, if you find DOM too frustrating, switch to one of the better tree models like JDOM2 or XOM.
Also, if you used an XPath 2.0 engine like Saxon, it would (a) simplify your expression to
//*:message-resource//*:code][contains(text(), 'account')]
and (b) allow you to return a sequence of strings from the XPath expression, rather than a sequence of nodes, so you wouldn't have to mess around with nodelists.
Another point: I suspect that the predicate [contains(text(), 'account')] should really be [.='account']. I'm not sure of that, but using text() instead of ".", and using contains() instead of "=", are both common mistakes.
I am receiving a xml in string format. Is there any library to search for elements in the string?
<Version value="0"/>
<IssueDate>2017-12-15</IssueDate>
<Locale>en_US</Locale>
<RecipientAddress>
<Category>Primary</Category>
<SubCategory>0</SubCategory>
<Name>Vitsi</Name>
<Attention>Stowell Group Llc.</Attention>
<AddressLine1>511 6th St</AddressLine1>
<City>Lake Oswego</City>
<Country>United States</Country>
<PresentationValue>Lake Oswego OR 97034-2903</PresentationValue>
<State>OR</State>
<ZIPCode>97034</ZIPCode>
<ZIP4>2903</ZIP4>
</RecipientAddress>
<RecipientAddress>
<Category>Additional</Category>
<SubCategory>1</SubCategory>
<Name>Vitsi</Name>
<AddressLine1>Po Box 957</AddressLine1>
<City>Lake Oswego</City>
<Country>United States</Country>
<PresentationValue>Lake Oswego OR 97034-0104</PresentationValue>
<State>OR</State>
<ZIPCode>97034</ZIPCode>
<ZIP4>0104</ZIP4>
</RecipientAddress>
<SenderName>TMO</SenderName>
<SenderId>IL</SenderId>
<SenderAddress>
<Name>T-mobile</Name>
<AddressLine1>Po Box 790047</AddressLine1>
<City>St. Louis</City>
<PresentationValue>ST. LOUIS MO 63179-0047</PresentationValue>
<State>MO</State>
<ZIPCode>63179</ZIPCode>
.
.
.
.
I want to access the element RecipientAddress, which is a list. Is there any library to do that? Please note that what I receive is a string. It is an invoice and there will be many to process, so performance is important
Following options are available:
Convert xml string to java objects using JAXB.
Use .indexOf() in string method to retrieve specific parts of xml.
Use regular expression to retrieve specific parts of xml.
SAX/DOM/STAX parser for parsing and extraction from xml.
Xpath for fetching the specific values from xml.
You could use XPATH. Java has inbuilt support for XML querying without any thirdparty library,
Code piece would be,
String xmlInputStr = "<YOUR_XML_STRING_INPUT>"
String xpathExpressionStr = "<XPATH_EXPRESSION_STRING>"
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(xmlInputStr);
XPathFactory xPathfactory = XPathFactory.newInstance();
XPath xpath = xPathfactory.newXPath();
XPathExpression expr = xpath.compile(xpathExpressionStr);
You can write your own expression string for querying. Typical example
"/RecipientAddress/Category"
Evaluate your xml against expression to retrieve list of nodes.
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
And iterate over nodes,
for (int i = 0; i < nodes.getLength(); i++) {
Node nNode = nodes.item(i);
...
}
There lot of pre-implemented api is available to convert xml to java object.
please look at that the xerces from Apache.
If you want extract only specified value the put whole in to string and use indexOf("string")
<Request>
<EMPId>?</EMPId>
</Request>
I know this is a repeated question, but i would like to post it again as i dint get a convincing answer from any of the threads i went through.
My ultimate aim is to add the XML given above as the Body content of a SOAP message.
You can have a look at the following link to see how i am doing it.
Namespace related error on creating SOAP Request
It worked fine when i was using the Websphere Application Server 7.0 library.JRE is also present, forgot to include in screen shot.
Since i have to export it as a jar and run it as a stand alone application, i have to remove the dependency of 'Websphere Application Server 7.0 library'. Because, by keeping this library, my jar size will go above 100MB. So i thought of taking only the library which i needed.
'com.ibm.ws.prereq.soap.jar'
Now the issue is, the Request tag of the generated SOAP request is coming in following format.
<Request xmlns="">
<EMPId>?</EMPId>
</Request>
I am able to create a 'org.w3c.dom.Document' representation for the generated SOAP message.
Now, can any one tell me how can I delete the xmlns="" from Request tag.
The simplest way what i found is:
first:
in child set nasmespace as in root:
second:
remove namespace
Document doc = new Document();
Namespace xmlns = Namespace.getNamespace("http://www.microsoft.com/networking/WLAN/profile/v1");
Element rootXML = new Element("WLANProfile", xmlns);
Element nameXML = new Element("name");
nameXML.addContent(name);
rootXML.addContent(nameXML);
//below solution
nameXML.setNamespace(xmlns);
nameXML.removeNamespaceDeclaration(xmlns);
Finally I found several solutions of the described problem.
First, you can remove all namespaces from all xml using this answer.
Second, if you do not need to remove all namespaces in Xml, but only empty ones, they arise due to the fact that some namespace is written in the root elements, which is not in the child. For example:
<ЭДПФР xmlns="http://пф.рф/КСАФ/2018-04-03"
xmlns:АФ4="xx"...>
<КСАФ xmlns="">
...
</КСАФ>
So you need to set the same namespace for all children of root elements. It can be done using this code (call setTheSameNamespaceForChildren(rootElement) for root element before saving):
private static final String namespaceKey = "xmlns";
private static String namespaceValue;
public static void setTheSameNamespaceForChildren(Element rootEl) {
namespaceValue = rootEl.getAttribute(namespaceKey);
NodeList list = rootEl.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node child = list.item(i);
setTheSameNamespaceRecursively(child);
}
}
private static void setTheSameNamespaceRecursively(Node node) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
boolean isChanged = setTheSameNamespace((Element) node);
if (isChanged) {
NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node child = list.item(i);
setTheSameNamespaceRecursively(child);
}
}
}
}
private static boolean setTheSameNamespace(Element node) {
String curValue = node.getAttribute(namespaceKey);
if (curValue.length() == 0) {
node.setAttribute(namespaceKey, namespaceValue);
return true;
}
return false;
}
I am currently working on an academic project, developing in Java and XML. Actual task is to parse XML, passing required values preferably in HashMap for further processing. Here is the short snippet of actual XML.
<root>
<BugReport ID = "1">
<Title>"(495584) Firefox - search suggestions passes wrong previous result to form history"</Title>
<Turn>
<Date>'2009-06-14 18:55:25'</Date>
<From>'Justin Dolske'</From>
<Text>
<Sentence ID = "3.1"> Created an attachment (id=383211) [details] Patch v.2</Sentence>
<Sentence ID = "3.2"> Ah. So, there's a ._formHistoryResult in the....</Sentence>
<Sentence ID = "3.3"> The simple fix it to just discard the service's form history result.</Sentence>
<Sentence ID = "3.4"> Otherwise it's trying to use a old form history result that no longer applies for the search string.</Sentence>
</Text>
</Turn>
<Turn>
<Date>'2009-06-19 12:07:34'</Date>
<From>'Gavin Sharp'</From>
<Text>
<Sentence ID = "4.1"> (From update of attachment 383211 [details])</Sentence>
<Sentence ID = "4.2"> Perhaps we should rename one of them to _fhResult just to reduce confusion?</Sentence>
</Text>
</Turn>
<Turn>
<Date>'2009-06-19 13:17:56'</Date>
<From>'Justin Dolske'</From>
<Text>
<Sentence ID = "5.1"> (In reply to comment #3)</Sentence>
<Sentence ID = "5.2"> > (From update of attachment 383211 [details] [details])</Sentence>
<Sentence ID = "5.3"> > Perhaps we should rename one of them to _fhResult just to reduce confusion?</Sentence>
<Sentence ID = "5.4"> Good point.</Sentence>
<Sentence ID = "5.5"> I renamed the one in the wrapper to _formHistResult. </Sentence>
<Sentence ID = "5.6"> fhResult seemed maybe a bit too short.</Sentence>
</Text>
</Turn>
.....
and so on
</BugReport>
There are many commenter like 'Justin Dolske' who have commented on this report and what I actually looking for is the list of commenter and all sentences they have written in a whole XML file. Something like if(from == justin dolske) getHisAllSentences(). Similarly for other commenters (for all). I have tried many different ways to get the sentences only for 'Justin dolske' or other commenters, even in a generic form for all using XPath, SAX and DOM but failed. I am quite new to these technologies including JAVA and any don't know how to achieve it.
Can anyone guide me specifically how could I get it with any of above technologies or is there any other better strategy to do it?
(Note: Later I want to put it in a hashmap such as like this HashMap (key, value) where key = name of commenter (justin dolske) and value is (all sentences))
Urgent help will be highly appreciated.
There're several ways using which you can achieve your requirement.
One way would be use JAXB. There're several tutorials available on this on the web, so feel free to refer to them.
You can also think of creating a DOM and then extracting data from it and then put it into your HashMap.
One reference implementation would be something like this:
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class XMLReader {
private HashMap<String,ArrayList<String>> namesSentencesMap;
public XMLReader() {
namesSentencesMap = new HashMap<String, ArrayList<String>>();
}
private Document getDocument(String fileName){
Document document = null;
try{
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(fileName));
}catch(Exception exe){
//handle exception
}
return document;
}
private void buildNamesSentencesMap(Document document){
if(document == null){
return;
}
//Get each Turn block
NodeList turnList = document.getElementsByTagName("Turn");
String fromName = null;
NodeList sentenceNodeList = null;
for(int turnIndex = 0; turnIndex < turnList.getLength(); turnIndex++){
Element turnElement = (Element)turnList.item(turnIndex);
//Assumption: <From> element
Element fromElement = (Element) turnElement.getElementsByTagName("From").item(0);
fromName = fromElement.getTextContent();
//Extracting sentences - First check whether the map contains
//an ArrayList corresponding to the name. If yes, then use that,
//else create a new one
ArrayList<String> sentenceList = namesSentencesMap.get(fromName);
if(sentenceList == null){
sentenceList = new ArrayList<String>();
}
//Extract sentences from the Turn node
try{
sentenceNodeList = turnElement.getElementsByTagName("Sentence");
for(int sentenceIndex = 0; sentenceIndex < sentenceNodeList.getLength(); sentenceIndex++){
sentenceList.add(((Element)sentenceNodeList.item(sentenceIndex)).getTextContent());
}
}finally{
sentenceNodeList = null;
}
//Put the list back in the map
namesSentencesMap.put(fromName, sentenceList);
}
}
public static void main(String[] args) {
XMLReader reader = new XMLReader();
reader.buildNamesSentencesMap(reader.getDocument("<your_xml_file>"));
for(String names: reader.namesSentencesMap.keySet()){
System.out.println("Name: "+names+"\tTotal Sentences: "+reader.namesSentencesMap.get(names).size());
}
}
}
Note: This is just a demonstration and you would need to modify it to suit your need. I've created it based on your XML to show one way of doing it.
I suggest to use JAXB to creates a Data Model reflecting your XML structure.
One done, you can load the XML into Java instances.
Put each 'Turn' into a Map< String, List< Turn >>, using Turn.From as key.
Once done, you'll can write:
List< Turn > justinsTurn = allTurns.get( "'Justin Dolske'" );
I want to parse xml elemets using java.I m succeeded in some part...But not sure how to do rest..I have xml as,
<MainTag>
<userid>user1</userid>
<country>US</country>
<city>LA</city>
<phone>
<number>1111111111</number>
</phone>
<phone>
<number>222222222</number>
</phone>
</MainTag>
<MainTag>
<userid>user2</userid>
<country>Aus</country>
<city>MB</city>
<phone>
<number>23233</number>
</phone>
<phone>
<number>8787822</number>
</phone>
<phone>
<number>10101</number>
</phone>
I am able to parse xml elements such as country,city etc as below.
public void endelement()
{
if (someText.equalsIgnoreCase("country"))
{
pojo.setCountry(Val);
}
else if(someText.equalsIgnoreCase("city"))
{
pojo.setCity(Val);
}
}
public void stratelement()
{
............
}
But in case of phone how I can parse it ? since one user has multiple phone nos.
I want to find multiple phone nos for particular user.
for e.g. in above xml
for user1 there are two phone nos.
for user2 there are three phone nos.
Can anybody help in this ? Thanks in advance.
I would recommend using JAXB, since it appears you are attempting to bind your xml to a POJO.
Looking at the code you have written here (and assuming that the example xml you have provided is a snippet of well formed xml), I am guess that your pojo object should have a member for phone numbers that is of type List<String>, and your pojo should have a method that allows you to add a phone number to the List (perhaps addPhoneNumber(String phoneNumber) {...})
First, that is not a well-formed XML (as it has two root elements) and you can't parse it with any parser API unless it is well-formed. Now, to parse the XML you would normally use the APIs meant for it like SAX, DOM or StAX or even better the JAXB binding API.
Since you seem to be new to this, I suggest you start learning JAXP. Use StAX instead of DOM or SAX.
you can use DocumetBuilderFactory java default class if you know the incoming xml format for example how many node it has and the names it is very simple look at this code ;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
//documentBuilder instance
DocumentBuilder db = dbf.newDocumentBuilder();
Document dom = db.parse("employees.xml");
}catch(ParserConfigurationException pce) {
pce.printStackTrace();
}catch(SAXException se) {
se.printStackTrace();
}catch(IOException ioe) {
ioe.printStackTrace();
}
//and than get root element
Element de= dom.getDocumentElement();
//get the nodelist of main element
NodeList nl = de.getElementsByTagName("Employee");
if(nl != null && nl.getLength() > 0) {
for(int i = 0 ; i < nl.getLength();i++) {
//get the employee element
Element el = (Element)nl.item(i);
}
}
//and then get data
private void getEmployee(Element el) {
//for each <employee> element get values
String name = getTextValue(el,"Name");
int id = getIntValue(el,"Id");
int age = getIntValue(el,"Age");
//get any element attribute
//String type = el.getAttribute("type");
}
thats all