I am using StAX Iterator api to read an xml.
XML:
<FormData OID="QUAL">
<IGData IGRepeatKey="1" IGOID="SQUAL" TType="Insert">
<IData Value="0859" IOID="SID"></IData>
<IData Value="DM" IOID="RDOMAIN"></IData>
</IGData>
<IGData IGRepeatKey="1" IGOID="SQUAL" TType="Insert">
<IData Value="0860" IOID="SID"></IData>
<IData Value="2013-01-03T02:00" IOID="QVAL"></IData>
</IGData>
</FormData>
And Stax code:
while(xmlEventReader.hasNext()){
xmlEvent = xmlEventReader.nextEvent();
eventString = xmlEvent.toString();
if(xmlEvent.isStartElement() && eventString.contains("FormData") && eventString.contains("QUAL")){
//do something
}
}
It is working (eventString has whole text of xmlEvent) in my local environment.
But when i deploy this into server, eventString contains like "Stax Event #1". So if condition is returning false.
I thought both are using different XMLEvent implementations. So i checked it through code, and jar is same in both environments: jre1.8.0_73/lib/rt.jar!/javax/xml/stream/events/XMLEvent.class
How to get the whole text of XMLEvent object? Am i doing anything wrong here? Please suggest any other alternatives.
Every XML Event has 3 states
Start Element
is Characters
End Element
for eg if you need to access the data for " IGRepeatKey " from your xml file then in the state ( Start Element ), you need to check if IGData tag has started , if its true . Start a new Iterator which will iterate over all the tags i.e IGRepeatKey , IGOID , TType .
Try Something like this
Iterator<Attribute> iterator = element.getAttributes();
while (iterator.hasNext())
{
Attribute attribute = (Attribute)iterator.next();
QName name = attribute.getName();
String value = attribute.getValue();
System.out.println(name+" + "+value);
}
Add this iterator in xml.isStartElement() block .
Related
I have an xml file from which i read using an XMLStreamReader object.
So i'll keep it simple :
Let's take this xml example :
<mySample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" attribute1="value1"/>
So what i need is to get the value (as a String) "xmlns:xsi" and get the value (as a String also) "http://www.w3.org/2001/XMLSchema-instance"
I did try to have a test like this :
if (reader.getEventType() != XMLStreamConstants.NAMESPACE){
attributeName = reader.getAttributeLocalName(i);
attributeValue = reader.getAttributeValue(i);
}
else{
attributeName = reader.getNamespacePrefix(i) + reader.getNamespaceURI(i);
attributeValue = reader.getAttributeValue(i);
}
But it did not work.
Obviously i missed something being a newbie to this API, so any help would be very welcome.
The JSR-173 specification (Stax API for Java) states the following regarding the NAMESPACE event :
Namespace
Namespace declarations can also exist outside of a StartElement and may be reported as a
standalone information item. In general Namespaces are reported as part of a StartElement
event. When namespaces are the result of an XQuery or XPath expression they may be
reported as standalone events.
So if you are looking at namespace events, you should most probably be checking StartElement events, and inspect them. Once again, from the spec :
Namespaces can be accessed using the following methods:
int getNamespaceCount();
String getNamespacePrefix(int index);
String getNamespaceURI(int index);
Only the namespaces declared on the current StartElement are available. The list does
not contain previously declared namespaces and does not remove redeclared namespaces.
At any point during the parsing, you can get the current complete namespace context :
The namespace context of the current state is available by calling
XMLStreamReader.getNamespaceContext() or
StartElement.getNamespaceContext(). These methods return an instance of the
javax.xml.namespace.NamespaceContext interface.
That's theory : most namespace declarations come from START_ELEMENT, some may come independently.
In practice, I have never came accross a NAMESPACE event reported by the API when reading from a file. It's almost always reported as part of a START_ELEMENT (and repeated in the corresponding END_ELEMENT), so you must check START_ELEMENT if you are interested in namespace declaration. For example, starting with your document :
String xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><mySample xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" attribute1=\"value1\"/>";
XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader(new StringReader(xml));
while (reader.hasNext()) {
int event = reader.next();
if (XMLStreamConstants.START_ELEMENT == event) {
if (reader.getNamespaceCount() > 0) {
// This happens
System.out.println("ELEMENT START: " + reader.getLocalName() + " , namespace count is: " + reader.getNamespaceCount());
for (int nsIndex = 0; nsIndex < reader.getNamespaceCount(); nsIndex++) {
String nsPrefix = reader.getNamespacePrefix(nsIndex);
String nsId = reader.getNamespaceURI(nsIndex);
System.out.println("\tNamepsace prefix: " + nsPrefix + " associated with URI " + nsId);
}
}
} else if(XMLStreamConstants.NAMESPACE == event) {
// This almost never happens
System.out.println("NAMESPACE EVENT");
}
}
Will produce :
ELEMENT START: mySample , namespace count is: 1
Namepsace prefix: xsi associated with URI http://www.w3.org/2001/XMLSchema-instance
Bottom line : you should check for NAMESPACE and START_ELEMENT events, even if most of times, you will only have START_ELEMENT reporting namespace declartions, it is not one or the other, it's both.
been scratching my head for a while here...
So I have a Java application. In this application I need to read an XML file, get the Character Data from an element, pass it into a new DOM Document, change some of the elements, and convert the new Document back into CDATA, reattach it to the original message and send it off.
So... Here is the message I need to read in, and the function which reads it in:
private static String getCharacterDataFromElement(Node e) {
Node child = e.getFirstChild();
if (child instanceof CharacterData) {
CharacterData cd = (CharacterData) child;
System.out.println(cd.getBaseURI());
System.out.println(cd.getData());
return cd.getBaseURI();
}
return "error...";
}
And here is the xml file which needs to be changed
<RLSOLVE_MSG version="5.0">
<MESSAGE>
<SOURCE_ID>DP01</SOURCE_ID>
<TRANS_NUM>000001</TRANS_NUM>
</MESSAGE>
<POI_MSG type="interaction">
<INTERACTION name="posPrintReceipt">
<RECEIPT type="merchant" format="xml">
<![CDATA[<RECEIPT>
<AUTH_CODE>06130</AUTH_CODE>
<CARD_SCHEME>VISA</CARD_SCHEME>
<CURRENCY_CODE>GBP</CURRENCY_CODE>
<CUSTOMER_PRESENCE>internet</CUSTOMER_PRESENCE>
<FINAL_AMOUNT>1.00</FINAL_AMOUNT>
<MERCHANT_NUMBER>8888888</MERCHANT_NUMBER>
<PAN_NUMBER>454420******0382</PAN_NUMBER>
<PAN_EXPIRY>12/15</PAN_EXPIRY>
<TERMINAL_ID>04176421</TERMINAL_ID>
<TOKEN>454420bbbbbkqrm0382</TOKEN>
<TOTAL_AMOUNT>1.00</TOTAL_AMOUNT>
<TRANSACTION_DATA_SOURCE>keyed</TRANSACTION_DATA_SOURCE>
<TRANSACTION_DATE>14/02/2014</TRANSACTION_DATE>
<TRANSACTION_NUMBER>000001</TRANSACTION_NUMBER>
<TRANSACTION_RESPONSE>06130</TRANSACTION_RESPONSE>
<TRANSACTION_TIME>17:13:17</TRANSACTION_TIME>
<TRANSACTION_TYPE>purchase</TRANSACTION_TYPE>
<VERIFICATION_METHOD>unknown</VERIFICATION_METHOD>
<DUPLICATE>false</DUPLICATE>
</RECEIPT>]]>
</RECEIPT>
</INTERACTION>
</POI_MSG>
</RLSOLVE_MSG>
When cd.getData() is executed, it returns "\n \t \t \t \t"
Aaaany ideas?
Look closely at your XML. If I write them in one line it's actually
<RECEIPT type="merchant" format="xml">\n\t\t\t<![CDATA[...]]>\n\t\t\t</RECEIPT>
So the tree will actually look something like:
RECEIPT
/ | \
\n\t\t\t CDATA \n\t\t\t
So you got three children. As you are only getting the first child, you're only getting \n\t\t\t.
Loop through all the children and concat their data and you should have everything.
I'm parsing a xml string with dom4j and I'm using xpath to select some element from it, the code is :
String test = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><epp xmlns=\"urn:ietf:params:xml:ns:epp-1.0\"><response><result code=\"1000\"><msg lang=\"en-US\">Command completed successfully</msg></result><trID><clTRID>87285586-99412370</clTRID><svTRID>52639BB8-1-ARNES</svTRID></trID></response></epp>";
SAXReader reader = new SAXReader();
reader.setIncludeExternalDTDDeclarations(false);
reader.setIncludeInternalDTDDeclarations(false);
reader.setValidation(false);
Document xmlDoc;
try {
xmlDoc = reader.read(new StringReader(test));
xmlDoc.getRootElement();
Node nodeStatus = xmlDoc.selectSingleNode("//epp/response/result");
System.out.print(nodeStatus.getText());
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I always get null for the nodeStatus variable. I actualy nead to read the code from the result noad from the xml
<result code="1000">
This is the XML that I am reading from the String test:
<?xml version="1.0" encoding="UTF-8"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<response>
<result code="1000">
<msg lang="en-US">Command completed successfully</msg>
</result>
<trID>
<clTRID>87285586-99412370</clTRID>
<svTRID>52639BB8-1-ARNES</svTRID>
</trID>
</response>
</epp>
Any hints?
Your XML has a namespace. DOM4J returns null because it won't find your nodes.
To make it work, you first have to register the namespaces you are using. You will need a prefix. Any one. And you will have to use that prefix in your XPath.
You could use tns for "target namespace". Then you have to create a xpath object with it like this:
XPath xpath = new DefaultXPath("/tns:epp/tns:response/tns:result");
To register the namespaces you will need to create a Map, add the namespace with the prefix you used in the xpath expression, and pass it to the setNamespaceURIs() method.
namespaces.put("tns", "urn:ietf:params:xml:ns:epp-1.0");
xpath.setNamespaceURIs(namespaces);
Now you can call selectSingleNode, but you will call it on your XPath object passing the document as the argument:
Node nodeStatus = xpath.selectSingleNode(xmlDoc);
From there you can extract the data you need. getText() won't give you the data you want. If you want the contents of the result node as XML, you can use:
nodeStatus.asXML()
Edit: to retrieve just the code, change your XPath to:
/tns:epp/tns:response/tns:result/#code
And retrieve the result with
nodeStatus.getText();
I replaced the double slash // (which means descendant-or-self) with / since the expression contains the full path and / is more efficient. But if you only have one result node in your whole file, you can use:
//result/#code
to extract the data. It will match all descendants. If there is more than one result, it will return a node-set.
I want to retrieve all the xpaths from soap message at run time.
For example, if I have a soap message like
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Bodyxmlns:ns1="http://xmlns.oracle.com/TestAppln_jws/TestEmail/TestEmail">
<ns1:process>
<ns1:To></ns1:To>
<ns1:Subject></ns1:Subject>
<ns1:Body></ns1:Body>
</ns1:process>
</soap:Body>
</soap:Envelope>
then the possible xpaths from this soap message are
/soap:Envelope/soap:Body/ns1:process/ns1:To
/soap:Envelope/soap:Body/ns1:process/ns1:Subject
/soap:Envelope/soap:Body/ns1:process/ns1:Body
How can i retrive those with java?
Use the XPath type with a NamespaceContext.
Map<String, String> map = new HashMap<String, String>();
map.put("foo", "http://xmlns.oracle.com/TestAppln_jws/TestEmail/TestEmail");
NamespaceContext context = ...; //TODO: context from map
XPath xpath = ...; //TODO: create instance from factory
xpath.setNamespaceContext(context);
Document doc = ...; //TODO: parse XML
String toValue = xpath.evaluate("//foo:To", doc);
The double forward slash makes this expression match the first To element in the http://xmlns.oracle.com/TestAppln_jws/TestEmail/TestEmail in the given node. It does not matter that I used foo instead of ns1; the prefix mapping needs to match the one in the XPath expression, not the one in the document.
You can find further examples in Java: using XPath with namespaces and implementing NamespaceContext. You can find further examples of working with SOAP here.
Something like this could work:
string[] paths;
function RecurseThroughRequest(string request, string[] paths, string currentPath)
{
Nodes[] nodes = getNodesAtPath(request, currentPath);
//getNodesAtPath is an assumed function which returns a set of
//Node objects representing all the nodes that are children at the current path
foreach(Node n in nodes)
{
if(!n.hasChildren())
{
paths.Add(currentPath + "/" + n.Name);
}
else
{
RecurseThroughRequest(paths, currentPath + "/" + n.Name);
}
}
}
And then call the function with something like this:
string[] paths = new string[];
RecurseThroughRequest(request, paths, "/");
Of course that won't work out of the gates, but I think the theory is there.
I have a XmlDocument in java, created with the Weblogic XmlDocument parser.
I want to replace the content of a tag in this XMLDocument with my own data, or insert the tag if it isn't there.
<customdata>
<tag1 />
<tag2>mfkdslmlfkm</tag2>
<location />
<tag3 />
</customdata>
For example I want to insert a URL in the location tag:
<location>http://something</location>
but otherwise leave the XML as is.
Currently I use a XMLCursor:
XmlObject xmlobj = XmlObject.Factory.parse(a.getCustomData(), options);
XmlCursor xmlcur = xmlobj.newCursor();
while (xmlcur.hasNextToken()) {
boolean found = false;
if (xmlcur.isStart() && "schema-location".equals(xmlcur.getName().toString())) {
xmlcur.setTextValue("http://replaced");
System.out.println("replaced");
found = true;
} else if (xmlcur.isStart() && "customdata".equals(xmlcur.getName().toString())) {
xmlcur.push();
} else if (xmlcur.isEnddoc()) {
if (!found) {
xmlcur.pop();
xmlcur.toEndToken();
xmlcur.insertElementWithText("schema-location", "http://inserted");
System.out.println("inserted");
}
}
xmlcur.toNextToken();
}
I tried to find a "quick" xquery way to do this since the XmlDocument has an execQuery method, but didn't find it very easy.
Do anyone have a better way than this? It seems a bit elaborate.
How about an XPath based approach? I like this approach as the logic is super-easy to understand. The code is pretty much self-documenting.
If your xml document is available to you as an org.w3c.dom.Document object (as most parsers return), then you could do something like the following:
// get the list of customdata nodes
NodeList customDataNodeSet = findNodes(document, "//customdata" );
for (int i=0 ; i < customDataNodeSet.getLength() ; i++) {
Node customDataNode = customDataNodeSet.item( i );
// get the location nodes (if any) within this one customdata node
NodeList locationNodeSet = findNodes(customDataNode, "location" );
if (locationNodeSet.getLength() > 0) {
// replace
locationNodeSet.item( 0 ).setTextContent( "http://stackoverflow.com/" );
}
else {
// insert
Element newLocationNode = document.createElement( "location" );
newLocationNode.setTextContent("http://stackoverflow.com/" );
customDataNode.appendChild( newLocationNode );
}
}
And here's the helper method findNodes that does the XPath search.
private NodeList findNodes( Object obj, String xPathString )
throws XPathExpressionException {
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile( xPathString );
return (NodeList) expression.evaluate( obj, XPathConstants.NODESET );
}
How about an object oriented approach? You could deserialise the XML to an object, set the location value on the object, then serialise back to XML.
XStream makes this really easy.
For example, you would define the main object, which in your case is CustomData (I'm using public fields to keep the example simple):
public class CustomData {
public String tag1;
public String tag2;
public String location;
public String tag3;
}
Then you initialize XStream:
XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);
Now you can construct an object from XML, set the location field on the object and regenerate the XML:
CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);
How does that sound?
If you don't know the schema the XStream solution probably isn't the way to go. At least XStream is on your radar now, might come in handy in the future!
You should be able to do this with query
try
fn:replace(string,pattern,replace)
I am new to xquery myself and I have found it to be a painful query language to work with, but it does work quiet well once you get over the initial learning curve.
I do still wish there was an easier way which was as efficient?