in my case its not full xml instead of that i want to parse the part of one xml tag to be parsed.
<FILTERABLE>
<FILTER_ELEMENT ALIAS_NAME="roomnumber" JOINER="AND" LPAREN="false" OPERATOR="BEGINS" RPAREN="false" SEQNUM="1" VALUE="1001"/>
</FILTERABLE>
Please help to convert the code into java object.
ByteArrayInputStream bis = new ByteArrayInputStream(filterStrValue.getBytes("UTF-8"));
Document document = EntityCollectionXMLUtil.DomfromXML(new InputSource(bis), false);
Element rootElement = document.getDocumentElement();
rootElement.getElementsByTagName("FILTERABLE")
Need one java object as a pair of hash map contains below
FILTER_ELEMENT ALIAS_NAME = "roomnumber"
JOINER="AND"
LPAREN="false"
OPERATOR="BEGINS"
RPAREN="false"
SEQNUM="1"
VALUE="1001"
dom4j is an open source, Java-based library for parse XML documents. in this answer used dom4j api for parse the xml document. hence, add the dom4j.jar file into your application's classpath.
class of FILTER_ELEMENT
public class Filter_Element {
private String ALIAS_NAME;
private String JOINER;
private Boolean LPAREN;
private String OPERATOR;
private Boolean RPAREN;
private int SEQNUM;
private int VALUE;
public String getALIAS_NAME() {
return ALIAS_NAME;
}
public void setALIAS_NAME(String aLIAS_NAME) {
ALIAS_NAME = aLIAS_NAME;
}
public String getJOINER() {
return JOINER;
}
public void setJOINER(String jOINER) {
JOINER = jOINER;
}
public Boolean getLPAREN() {
return LPAREN;
}
public void setLPAREN(Boolean lPAREN) {
LPAREN = lPAREN;
}
public String getOPERATOR() {
return OPERATOR;
}
public void setOPERATOR(String oPERATOR) {
OPERATOR = oPERATOR;
}
public Boolean getRPAREN() {
return RPAREN;
}
public void setRPAREN(Boolean rPAREN) {
RPAREN = rPAREN;
}
public int getSEQNUM() {
return SEQNUM;
}
public void setSEQNUM(int sEQNUM) {
SEQNUM = sEQNUM;
}
public int getVALUE() {
return VALUE;
}
public void setVALUE(int vALUE) {
VALUE = vALUE;
}
}
attributes values of xml element (FILTER_ELEMENT) set to the filterElement object
try {
File fXmlFile = new File("your_xml_file.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fXmlFile);
NodeList nodeList= doc.getElementsByTagName("FILTER_ELEMENT");
Filter_Element filterElement;
for(int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
filterElement = new Filter_Element();
filterElement.setALIAS_NAME(node.getAttributes().getNamedItem("ALIAS_NAME").getNodeValue());
filterElement.setJOINER(node.getAttributes().getNamedItem("JOINER").getNodeValue());
filterElement.setLPAREN(Boolean.valueOf(node.getAttributes().getNamedItem("LPAREN").getNodeValue()));
filterElement.setOPERATOR(node.getAttributes().getNamedItem("OPERATOR").getNodeValue());
filterElement.setRPAREN(Boolean.valueOf(node.getAttributes().getNamedItem("RPAREN").getNodeValue()));
filterElement.setSEQNUM(Integer.valueOf(node.getAttributes().getNamedItem("SEQNUM").getNodeValue()));
filterElement.setVALUE(Integer.valueOf(node.getAttributes().getNamedItem("VALUE").getNodeValue()));
}
} catch (Exception e) {
e.printStackTrace();
}
Related
I would like to get searchedProduct name from ArrayList example in new Product class, how to do it? Everything works correct but only I forgot how to get searchedProduct for example in another class :(
public class XMLoader {
private final String XML_PATH = "src\\main\\java\\products.xml";
private List<SearchData> data = new ArrayList<SearchData>();
public XMLoader() throws ParserConfigurationException, IOException, SAXException {
File inputFile = new File(XML_PATH);
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(inputFile);
NodeList nodeList = document.getDocumentElement().getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element element = (Element) node;
String id = node.getAttributes().getNamedItem("ID").getNodeValue();
String searchedProduct = element.getElementsByTagName("Category").item(0).getChildNodes().item(0).getNodeValue();
data.add(new SearchData(id, searchedProduct));
}
}
}
public class SearchData {
private String id;
private String searchedProduct;
public SearchData( String id, String searchedProduct) {
this.searchedProduct = searchedProduct;
this.id = id;
}
public String getSearchedProduct() {
return searchedProduct;
}
public String getId() {
return id;
}
#Override
public String toString() {
return "SearchData{" +
"id='" + id + '\'' +
", searchedProduct='" + searchedProduct + '\'' +
'}';
}
}
There are two ways I can immediately see (I leave it up to you to re-write it to modern Java):
public String getIdFromSearchData(String searchedProduct) {
// iterate over your list
for ( SearchData element : data ) {
// compare searchedProduct to the paramater
if ( searchedProduct.equals(element.getSearchedProduct()) {
return element.getId();
}
}
// in case nothing found
return null;
}
or, you override the equals method in SearchData to compare the searchedProduct (only)
public boolean equals(Object o) {
// let's assume all checks have been done
SearchData data = (SearchData)o;
return data.getSearchedProduct().equals(searchedProduct);
}
at which point you can immediately find the element from the list:
public String getIdFromSearchData(String searchedProduct) {
SearchData d = new SeardData(null, searchedProduct);
if ( data.contains(d) ) {
d = data.get(data.indexOf(d));
return d.getId();
}
return null;
}
A few remarks about this second option:
1. I would strongly recommend against it. One day you might have to compare on the id as well, and at that point, your code will no longer function
2. If you decide to do something like this second way anyway, don't forget to implement the hashCode() method as well.
I know I can hardcode it (parse xml, extract), but is there a way to feed (attribute value + nodevalue) like it is done with Feed4TestNG (it currently support only csv, and excel files)?
I am new to Java, and any expert insight would be helpful. Thanks!
The body of a #Parameters is not limited to data only, you are able to use any java code you like in this method, including throwing exceptions:
#Parameters
public static Collection<Object[]> data() throws IOException {
List<Object[]> data = new ArrayList<>();
// replace getClass() with <nameofclass>.class
try(InputStream in = this.getClass().getResourceAsStream()) {
//parse body here
data.add(new Object[]{attribute, value});
}
return data;
}
Depending on what XML framework you use, you need to parse your XML nodes, and put it in the list, that you are going to return.
So this is what I end up doing here:
Please submit your correction if you think I can improve my code.
.
#RunWith(Parameterized.class)
public class DataDrivenTests {
private String c;
private String b;
private static Collection<Object[]> a;
#Parameters
public static Collection<Object[]> xmlData() throws IOException{
File file = new File("xmlfile.xml");
InputStream xml1 = new FileInputStream(file);
return new xmlData(xml1).getData();
}
public DataDrivenTests(String c, String b) {
super();
this.c = c;
this.b = b;
}
#Test
public void shouldCalculateATimesB() {
boolean assertion = false;
if(c.equals(Parser.parse("Parse this string to Attribute and Value"))){
assertion = true;
}
assertTrue(assertion);
}
}
xmlData.java
public class xmlData{
private transient Collection<Object[]> data = null;
public xmlData(final InputStream xml)throws IOException{
this.data = loadFromXml(xml);
}
public Collection<Object[]> getData(){
return data;
}
private Collection<Object[]> loadFromXml(final InputStream xml)
throws IOException {
List <Object[]> ism_code_map = new ArrayList<Object[]>();
try{
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder dBuilder;
dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xml);
doc.getDocumentElement().normalize();
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile("//e");
NodeList nodes = (NodeList) expression.evaluate(doc, XPathConstants.NODESET);
for (int i =0; i< nodes.getLength(); i++){
Node nNode = nodes.item(i);
//System.out.println("\nCurrent Element:" + nNode.getTextContent());
if (nNode.getNodeType() == Node.ELEMENT_NODE){
Element eElement = (Element) nNode;
if(eElement.getAttribute("attrname") != null && !eElement.getAttribute("attrname").isEmpty()){
code_map.add(new Object[]{"attrname",eElement.getAttribute("attrname")});
}
}catch(ParserConfigurationException e){
e.printStackTrace();
}catch(SAXException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}catch(XPathExpressionException e){
e.printStackTrace();
}
catch(NullPointerException e){
e.printStackTrace();
}
return code_map;
}
}
i have a class which is returning a string type value and i want to return an String array, so please tell how can i able to do that
i have an xml file like resource.xml
<prompts>
<prompt id="p1">welcome to</prompt>
<prompt id ="p2">stack overflow</prompt>
<prompt id="p3">You entered</prompt>
<prompt id="p4">the correct number</prompt>
<prompts>
i am parsing it using sax parser
public class XmlReaderPrompt {
public List<PromptBean> load(String langMode)
{
String fileName="resource.xml";
DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
InputStream prompt_configfile=Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
DocumentBuilder db = null;
List<PromptBean> promptMap = new ArrayList<PromptBean>();
try {
try {
db = dbf.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document doc = null;
try {
doc = db.parse(prompt_configfile);
}
catch (SAXException e) {
e.printStackTrace();
}
NodeList nodeList=doc.getElementsByTagName("prompt");
for(int i=0;i<nodeList.getLength();i++)
{
Node node=nodeList.item(i);
if(node.getNodeType()==Node.ELEMENT_NODE)
{
Element element=(Element)node;
String id = element.getAttribute("id");
String name = element.getAttribute("name");
String prompt=getTextValue(element);
promptMap.add(new PromptBean(id,name,prompt));
}
}
}
catch(Exception io)
{
io.printStackTrace();
}
finally
{
db=null;
dbf=null;
}
return promptMap;
}
private String getTextValue(Element element) {
String textValue=element.getFirstChild().getTextContent().toString();
return textValue;
}
}
and a UserFunction class to return the text from the xml file
public class UserFunction{
List<PromptBean> promptObject = new ArrayList<PromptBean>();
public String getPromptFunction(String promptTag,String langMode )
{
List<PromptBean> promptObject=xrpObject.load(langMode);
for (Iterator<PromptBean> iterator = promptObject.iterator(); iterator.hasNext();){
PromptBean promptBean= (PromptBean)iterator.next();
if(promptBean.getId().equalsIgnoreCase(promptTag)){
return StringEscapeUtils.escapeXml(promptBean.getPrompt());
}
}
return null;
}
The problem is that I have to call the method getPromptFunction of UserFunction class every time I need to get text from the sub element like
String pr1 = UserFunction.getPromptFunction("p1" "resource");
String pr1 = UserFunction.getPromptFunction("p2" "resource");
String pr1 = UserFunction.getPromptFunction("p3" "resource");
and using it in jsp page as <%=pr1%>
So I want to use array like
String[] pr = UserFunction.getPromptFunction('"p1","p2","p3"' "resource")
So how I am able to do that and also tell how to use it in jsp page .
You can do it like this
public String[] getPromptFunction(String promptTag,String langMode )
{
String temp[] = new String[promptObject.size()];
List<PromptBean> promptObject=xrpObject.load(langMode);
int i = 0;
for (Iterator<PromptBean> iterator = promptObject.iterator(); iterator.hasNext();) {
PromptBean promptBean= (PromptBean)iterator.next();
if(promptBean.getId().equalsIgnoreCase(promptTag)){
temp[i] = StringEscapeUtils.escapeXml(promptBean.getPrompt());
}
i++;
}
return temp;
}
I'm running a small Android project which could read RSS/Atom Feed documents, using SAX library. Everything works well for default RSS sources, but with minimized sources (without spaces or new line tokens), it produces nothing but a list of blank items. My logs in Log cat also display nothing. I double check this problems with variant RSS sites, but problems still there. Below is my inheritance class of DefaultHandler which I use to handle Rss sources
public class RssContentHandler extends DefaultHandler {
private static final int UNKNOWN_STATE = -1;
private static final int ELEMENT_START = 0;
private static final int TITLE_END = 1;
private static final int DESCRIPTION_END = 2;
private static final int LINK_END = 3;
private static final int PUBDATE_END = 4;
private static final int CHANNEL_END = 5;
private int iState = UNKNOWN_STATE;
private String fullCharacters;
private boolean itemFound = false;
private RssItem rssItem;
private RssFeed rssFeed;
public RssContentHandler() {
}
public RssFeed getFeed() {
return this.rssFeed;
}
#Override
public void startDocument() {
rssItem = new RssItem();
rssFeed = new RssFeed();
Log.i("startDocument", "startDocument");
}
#Override
public void endDocument() {
}
#Override
public void startElement(String _uri, String _localName, String _qName, Attributes _attributes) {
if (_localName.equalsIgnoreCase("item")) {
itemFound = true;
rssItem = new RssItem();
this.iState = UNKNOWN_STATE;
} else
this.iState = ELEMENT_START;
fullCharacters = "";
}
#Override
public void endElement(String _uri, String _localName, String _qName) {
if (_localName.equalsIgnoreCase("item"))
this.rssFeed.addItem(this.rssItem);
else if (_localName.equalsIgnoreCase("title"))
this.iState = TITLE_END;
else if (_localName.equalsIgnoreCase("description"))
this.iState = DESCRIPTION_END;
else if (_localName.equalsIgnoreCase("link"))
this.iState = LINK_END;
else if (_localName.equalsIgnoreCase("pubDate"))
this.iState = PUBDATE_END;
else if (_localName.equalsIgnoreCase("channel"))
this.iState = CHANNEL_END;
else
this.iState = UNKNOWN_STATE;
}
#Override
public void characters(char[] _ch, int _start, int _length) {
String strCharacters = new String(_ch, _start, _length);
if (this.iState == ELEMENT_START)
fullCharacters += strCharacters;
else {
if (!itemFound) {
switch (this.iState) {
case TITLE_END:
this.rssFeed.setTitle(fullCharacters);
break;
case DESCRIPTION_END:
this.rssFeed.setDescription(fullCharacters);
break;
case LINK_END:
this.rssFeed.setLink(fullCharacters);
break;
case PUBDATE_END:
this.rssFeed.setPubDate(fullCharacters);
break;
}
} else {
switch (this.iState) {
case TITLE_END:
this.rssItem.setTitle(fullCharacters);
Log.i("characters", fullCharacters);
break;
case DESCRIPTION_END:
this.rssItem.setDescription(fullCharacters);
break;
case LINK_END:
this.rssItem.setLink(fullCharacters);
break;
case PUBDATE_END:
this.rssItem.setPubDate(fullCharacters);
break;
}
}
this.iState = UNKNOWN_STATE;
}
}
}
and snippet to setup the parser:
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet();
try {
request.setURI(new URI(_strUrl));
} catch (URISyntaxException e) {
e.printStackTrace();
}
HttpResponse response = client.execute(request);
Reader inputStream = new InputStreamReader(response.getEntity().getContent());
RssContentHandler rssContentHandler = new RssContentHandler();
InputSource inputSource = new InputSource();
inputSource.setCharacterStream(inputStream);
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxParserFactory.newSAXParser();
saxParser.parse(inputSource, rssContentHandler);
this.rssFeed = rssContentHandler.getFeed();
P/s: i'm using Android 2.3 x86 installed on VirtualBox for Debugging, and these sources work fine with the built-in RSS Reader app come with the x86 version. So what's wrong here?
Try with _qName instead of _localName.
Your xml contains CDATA so You cann't parse the XML response with your current parser. You have to use LexicalHandler for parsing Raw HTML.
public class MyHandler implements LexicalHandler {
public void startDTD(String name, String publicId, String systemId)
throws SAXException {}
public void endDTD() throws SAXException {}
public void startEntity(String name) throws SAXException {}
public void endEntity(String name) throws SAXException {}
public void startCDATA() throws SAXException {}
public void endCDATA() throws SAXException {}
public void comment (char[] text, int start, int length)
throws SAXException {
String comment = new String(text, start, length);
System.out.println(comment);
}
You can also parse your XML with DOM if memory is not the issue. For more help visit Handling Lexical Events
I'm writing a client which needs to read multiple consecutive small XML documents over a socket. I can assume that the encoding is always UTF-8 and that there is optionally delimiting whitespace between documents. The documents should ultimately go into DOM objects. What is the best way to accomplish this?
The essense of the problem is that the parsers expect a single document in the stream and consider the rest of the content junk. I thought that I could artificially end the document by tracking the element depth, and creating a new reader using the existing input stream. E.g. something like:
// Broken
public void parseInputStream(InputStream inputStream) throws Exception
{
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLEventFactory eventFactory = XMLEventFactory.newInstance();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
XMLEventWriter domWriter = xof.createXMLEventWriter(new DOMResult(doc));
XMLStreamReader xmlStreamReader = factory.createXMLStreamReader(inputStream);
XMLEventReader reader = factory.createXMLEventReader(xmlStreamReader);
int depth = 0;
while (reader.hasNext()) {
XMLEvent evt = reader.nextEvent();
domWriter.add(evt);
switch (evt.getEventType()) {
case XMLEvent.START_ELEMENT:
depth++;
break;
case XMLEvent.END_ELEMENT:
depth--;
if (depth == 0)
{
domWriter.add(eventFactory.createEndDocument());
System.out.println(doc);
reader.close();
xmlStreamReader.close();
xmlStreamReader = factory.createXMLStreamReader(inputStream);
reader = factory.createXMLEventReader(xmlStreamReader);
doc = documentBuilder.newDocument();
domWriter = xof.createXMLEventWriter(new DOMResult(doc));
domWriter.add(eventFactory.createStartDocument());
}
break;
}
}
}
However running this on input such as <a></a><b></b><c></c> prints the first document and throws an XMLStreamException. Whats the right way to do this?
Clarification: Unfortunately the protocol is fixed by the server and cannot be changed, so prepending a length or wrapping the contents would not work.
Length-prefix each document (in bytes).
Read the length of the first document from the socket
Read that much data from the socket, dumping it into a ByteArrayOutputStream
Create a ByteArrayInputStream from the results
Parse that ByteArrayInputStream to get the first document
Repeat for the second document etc
IIRC, XML documents can have comments and processing-instructions at the end, so there's no real way of telling exactly when you have come to the end of the file.
A couple of ways of handling the situation have already been mentioned. Another alternative is to put in an illegal character or byte into the stream, such as NUL or zero. This has the advantage that you don't need to alter the documents and you never need to buffer an entire file.
just change to whatever stream
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
public class LogParser {
private XMLInputFactory inputFactory = null;
private XMLStreamReader xmlReader = null;
InputStream is;
private int depth;
private QName rootElement;
private static class XMLStream extends InputStream
{
InputStream delegate;
StringReader startroot = new StringReader("<root>");
StringReader endroot = new StringReader("</root>");
XMLStream(InputStream delegate)
{
this.delegate = delegate;
}
public int read() throws IOException {
int c = startroot.read();
if(c==-1)
{
c = delegate.read();
}
if(c==-1)
{
c = endroot.read();
}
return c;
}
}
public LogParser() {
inputFactory = XMLInputFactory.newInstance();
}
public void read() throws Exception {
is = new XMLStream(new FileInputStream(new File(
"./myfile.log")));
xmlReader = inputFactory.createXMLStreamReader(is);
while (xmlReader.hasNext()) {
printEvent(xmlReader);
xmlReader.next();
}
xmlReader.close();
}
public void printEvent(XMLStreamReader xmlr) throws Exception {
switch (xmlr.getEventType()) {
case XMLStreamConstants.END_DOCUMENT:
System.out.println("finished");
break;
case XMLStreamConstants.START_ELEMENT:
System.out.print("<");
printName(xmlr);
printNamespaces(xmlr);
printAttributes(xmlr);
System.out.print(">");
if(rootElement==null && depth==1)
{
rootElement = xmlr.getName();
}
depth++;
break;
case XMLStreamConstants.END_ELEMENT:
System.out.print("</");
printName(xmlr);
System.out.print(">");
depth--;
if(depth==1 && rootElement.equals(xmlr.getName()))
{
rootElement=null;
System.out.println("finished element");
}
break;
case XMLStreamConstants.SPACE:
case XMLStreamConstants.CHARACTERS:
int start = xmlr.getTextStart();
int length = xmlr.getTextLength();
System.out
.print(new String(xmlr.getTextCharacters(), start, length));
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
System.out.print("<?");
if (xmlr.hasText())
System.out.print(xmlr.getText());
System.out.print("?>");
break;
case XMLStreamConstants.CDATA:
System.out.print("<![CDATA[");
start = xmlr.getTextStart();
length = xmlr.getTextLength();
System.out
.print(new String(xmlr.getTextCharacters(), start, length));
System.out.print("]]>");
break;
case XMLStreamConstants.COMMENT:
System.out.print("<!--");
if (xmlr.hasText())
System.out.print(xmlr.getText());
System.out.print("-->");
break;
case XMLStreamConstants.ENTITY_REFERENCE:
System.out.print(xmlr.getLocalName() + "=");
if (xmlr.hasText())
System.out.print("[" + xmlr.getText() + "]");
break;
case XMLStreamConstants.START_DOCUMENT:
System.out.print("<?xml");
System.out.print(" version='" + xmlr.getVersion() + "'");
System.out.print(" encoding='" + xmlr.getCharacterEncodingScheme()
+ "'");
if (xmlr.isStandalone())
System.out.print(" standalone='yes'");
else
System.out.print(" standalone='no'");
System.out.print("?>");
break;
}
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
new LogParser().read();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void printName(XMLStreamReader xmlr) {
if (xmlr.hasName()) {
System.out.print(getName(xmlr));
}
}
private static String getName(XMLStreamReader xmlr) {
if (xmlr.hasName()) {
String prefix = xmlr.getPrefix();
String uri = xmlr.getNamespaceURI();
String localName = xmlr.getLocalName();
return getName(prefix, uri, localName);
}
return null;
}
private static String getName(String prefix, String uri, String localName) {
String name = "";
if (uri != null && !("".equals(uri)))
name += "['" + uri + "']:";
if (prefix != null)
name += prefix + ":";
if (localName != null)
name += localName;
return name;
}
private static void printAttributes(XMLStreamReader xmlr) {
for (int i = 0; i < xmlr.getAttributeCount(); i++) {
printAttribute(xmlr, i);
}
}
private static void printAttribute(XMLStreamReader xmlr, int index) {
String prefix = xmlr.getAttributePrefix(index);
String namespace = xmlr.getAttributeNamespace(index);
String localName = xmlr.getAttributeLocalName(index);
String value = xmlr.getAttributeValue(index);
System.out.print(" ");
System.out.print(getName(prefix, namespace, localName));
System.out.print("='" + value + "'");
}
private static void printNamespaces(XMLStreamReader xmlr) {
for (int i = 0; i < xmlr.getNamespaceCount(); i++) {
printNamespace(xmlr, i);
}
}
private static void printNamespace(XMLStreamReader xmlr, int index) {
String prefix = xmlr.getNamespacePrefix(index);
String uri = xmlr.getNamespaceURI(index);
System.out.print(" ");
if (prefix == null)
System.out.print("xmlns='" + uri + "'");
else
System.out.print("xmlns:" + prefix + "='" + uri + "'");
}
}
A simple solution is to wrap the documents on the sending side in a new root element:
<?xml version="1.0"?>
<documents>
... document 1 ...
... document 2 ...
</documents>
You must make sure that you don't include the XML header (<?xml ...?>), though. If all documents use the same encoding, this can be accomplished with a simple filter which just ignores the first line of each document if it starts with <?xml
Found this forum message (which you probably already saw), which has a solution by wrapping the input stream and testing for one of two ascii characters (see post).
You could try an adaptation on this by first converting to use a reader (for proper character encoding) and then doing element counting until you reach the closing element, at which point you trigger the EOM.
Hi
I also had this problem at work (so won't post resulting the code). The most elegant solution that I could think of, and which works pretty nicely imo, is as follows
Create a class for example DocumentSplittingInputStream which extends InputStream and takes the underlying inputstream in its constructor (or gets set after construction...).
Add a field with a byte array closeTag containing the bytes of the closing root node you are looking for.
Add a field int called matchCount or something, initialised to zero.
Add a field boolean called underlyingInputStreamNotFinished, initialised to true
On the read() implementation:
Check if matchCount == closeTag.length, if it does, set matchCount to -1, return -1
If matchCount == -1, set matchCount = 0, call read() on the underlying inputstream until you get -1 or '<' (the xml declaration of the next document on the stream) and return it. Note that for all I know the xml spec allows comments after the document element, but I knew I was not going to get that from the source so did not bother handling it - if you can not be sure you'll need to change the "gobble" slightly.
Otherwise read an int from the underlying inputstream (if it equals closeTag[matchCount] then increment matchCount, if it doesn't then reset matchCount to zero) and return the newly read byte
Add a method which returns the boolean on whether the underlying stream has closed.
All reads on the underlying input stream should go through a separate method where it checks if the value read is -1 and if so, sets the field "underlyingInputStreamNotFinished" to false.
I may have missed some minor points but i'm sure you get the picture.
Then in the using code you do something like, if you are using xstream:
DocumentSplittingInputStream dsis = new DocumentSplittingInputStream(underlyingInputStream);
while (dsis.underlyingInputStreamNotFinished()) {
MyObject mo = xstream.fromXML(dsis);
mo.doSomething(); // or something.doSomething(mo);
}
David
I had to do something like this and during my research on how to approach it, I found this thread that even though it is quite old, I just replied (to myself) here wrapping everything in its own Reader for simpler use
I was faced with a similar problem. A web service I'm consuming will (in some cases) return multiple xml documents in response to a single HTTP GET request. I could read the entire response into a String and split it, but instead I implemented a splitting input stream based on user467257's post above. Here is the code:
public class AnotherSplittingInputStream extends InputStream {
private final InputStream realStream;
private final byte[] closeTag;
private int matchCount;
private boolean realStreamFinished;
private boolean reachedCloseTag;
public AnotherSplittingInputStream(InputStream realStream, String closeTag) {
this.realStream = realStream;
this.closeTag = closeTag.getBytes();
}
#Override
public int read() throws IOException {
if (reachedCloseTag) {
return -1;
}
if (matchCount == closeTag.length) {
matchCount = 0;
reachedCloseTag = true;
return -1;
}
int ch = realStream.read();
if (ch == -1) {
realStreamFinished = true;
}
else if (ch == closeTag[matchCount]) {
matchCount++;
} else {
matchCount = 0;
}
return ch;
}
public boolean hasMoreData() {
if (realStreamFinished == true) {
return false;
} else {
reachedCloseTag = false;
return true;
}
}
}
And to use it:
String xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<root>first root</root>" +
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<root>second root</root>";
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes());
SplittingInputStream splitter = new SplittingInputStream(is, "</root>");
BufferedReader reader = new BufferedReader(new InputStreamReader(splitter));
while (splitter.hasMoreData()) {
System.out.println("Starting next stream");
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println("line ["+line+"]");
}
}
I use JAXB approach to unmarshall messages from multiply stream:
MultiInputStream.java
public class MultiInputStream extends InputStream {
private final Reader source;
private final StringReader startRoot = new StringReader("<root>");
private final StringReader endRoot = new StringReader("</root>");
public MultiInputStream(Reader source) {
this.source = source;
}
#Override
public int read() throws IOException {
int count = startRoot.read();
if (count == -1) {
count = source.read();
}
if (count == -1) {
count = endRoot.read();
}
return count;
}
}
MultiEventReader.java
public class MultiEventReader implements XMLEventReader {
private final XMLEventReader reader;
private boolean isXMLEvent = false;
private int level = 0;
public MultiEventReader(XMLEventReader reader) throws XMLStreamException {
this.reader = reader;
startXML();
}
private void startXML() throws XMLStreamException {
while (reader.hasNext()) {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
return;
}
}
}
public boolean hasNextXML() {
return reader.hasNext();
}
public void nextXML() throws XMLStreamException {
while (reader.hasNext()) {
XMLEvent event = reader.peek();
if (event.isStartElement()) {
isXMLEvent = true;
return;
}
reader.nextEvent();
}
}
#Override
public XMLEvent nextEvent() throws XMLStreamException {
XMLEvent event = reader.nextEvent();
if (event.isStartElement()) {
level++;
}
if (event.isEndElement()) {
level--;
if (level == 0) {
isXMLEvent = false;
}
}
return event;
}
#Override
public boolean hasNext() {
return isXMLEvent;
}
#Override
public XMLEvent peek() throws XMLStreamException {
XMLEvent event = reader.peek();
if (level == 0) {
while (event != null && !event.isStartElement() && reader.hasNext()) {
reader.nextEvent();
event = reader.peek();
}
}
return event;
}
#Override
public String getElementText() throws XMLStreamException {
throw new NotImplementedException();
}
#Override
public XMLEvent nextTag() throws XMLStreamException {
throw new NotImplementedException();
}
#Override
public Object getProperty(String name) throws IllegalArgumentException {
throw new NotImplementedException();
}
#Override
public void close() throws XMLStreamException {
throw new NotImplementedException();
}
#Override
public Object next() {
throw new NotImplementedException();
}
#Override
public void remove() {
throw new NotImplementedException();
}
}
Message.java
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Message")
public class Message {
public Message() {
}
#XmlAttribute(name = "ID", required = true)
protected long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
#Override
public String toString() {
return "Message{id=" + id + '}';
}
}
Read multiply messages:
public static void main(String[] args) throws Exception{
StringReader stringReader = new StringReader(
"<Message ID=\"123\" />\n" +
"<Message ID=\"321\" />"
);
JAXBContext context = JAXBContext.newInstance(Message.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
XMLInputFactory inputFactory = XMLInputFactory.newFactory();
MultiInputStream multiInputStream = new MultiInputStream(stringReader);
XMLEventReader xmlEventReader = inputFactory.createXMLEventReader(multiInputStream);
MultiEventReader multiEventReader = new MultiEventReader(xmlEventReader);
while (multiEventReader.hasNextXML()) {
Object message = unmarshaller.unmarshal(multiEventReader);
System.out.println(message);
multiEventReader.nextXML();
}
}
results:
Message{id=123}
Message{id=321}