Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read XML String using StAX

I am using stax for the first time to parse an XML String. I have found some examples but can't get my code to work. This is the latest version of my code:

public class AddressResponseParser
{
    private static final String STATUS = "status";
    private static final String ADDRESS_ID = "address_id";
    private static final String CIVIC_ADDRESS = "civic_address";

    String status = null;
    String addressId = null;
    String civicAddress = null;

    public static AddressResponse parseAddressResponse(String response)
    {

        try
        {
            byte[] byteArray = response.getBytes("UTF-8");
            ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);
            XMLInputFactory inputFactory = XMLInputFactory.newInstance();
            XMLStreamReader reader = inputFactory.createXMLStreamReader(inputStream);

            while (reader.hasNext())
            {
                int event = reader.next();

                if (event == XMLStreamConstants.START_ELEMENT)
                {
                    String element = reader.getLocalName();

                    if (element.equals(STATUS))
                    {
                        status = reader.getElementText();
                        continue;
                    }

                    if (element.equals(ADDRESS_ID))
                    {
                        addressId = reader.getText();
                        continue;
                    }

                    if (element.equals(CIVIC_ADDRESS))
                    {
                        civicAddress = reader.getText();
                        continue;
                    }
                }
            }
        }
        catch (Exception e)
        {
            log.error("Couldn't parse AddressResponse", e);
        }
    }
}

I've put watches on "event" and "reader.getElementText()". When the code is stopped on

String element = reader.getLocalName();

the "reader.getElementText()" value is displayed, but as soon as it moves away from that line it can't be evaluated. When the code is stopped on:

status = reader.getElementText();

the "element" watch displays the correct value. Finally, when I step the code one more line, I catch this exception:

(com.ctc.wstx.exc.WstxParsingException) com.ctc.wstx.exc.WstxParsingException: Current state not START_ELEMENT
 at [row,col {unknown-source}]: [1,29]

I've tried using status = reader.getText(); instead, but then I get this exception:

(java.lang.IllegalStateException) java.lang.IllegalStateException: Not a textual event (END_ELEMENT)

Can somebody point out what I'm doing wrong??

EDIT:

Adding JUnit code used to test:

public class AddressResponseParserTest
{
    private String status = "OK";
    private String address_id = "123456";
    private String civic_address = "727";

    @Test
    public void testAddressResponseParser() throws UnsupportedEncodingException, XMLStreamException
    {
        AddressResponse parsedResponse = AddressResponseParser.parseAddressResponse(this.responseXML());

        assertEquals(this.status, parsedResponse.getStatus());

        assertEquals(this.address_id, parsedResponse.getAddress()
                .getAddressId());
        assertEquals(this.civic_address, parsedResponse.getAddress()
                .getCivicAddress());
    }

    private String responseXML()
    {
        StringBuffer buffer = new StringBuffer();

        buffer.append("<response>");
        buffer.append("<status>OK</status>");
        buffer.append("<address>");
        buffer.append("<address_id>123456</address_id>");
        buffer.append("<civic_address>727</civic_address>");
        buffer.append("</address>");
        buffer.append("</response>");

        return buffer.toString();
    }
}
like image 585
sdoca Avatar asked Jan 10 '11 22:01

sdoca


People also ask

How to Read XML File in Java using StAX parser?

XMLEventReader reads an XML file as a stream of events. isStartElement(): checks if the current event is a StartElement (start tag) isEndElement(): checks if the current event is an EndElement (end tag) asCharacters(): returns the current event as characters. getName(): gets the name of the current event.

How does StAX parser work?

It means in case of StAX parser, a client application needs to ask the StAX parser to get information from XML whenever it needs. But in case of SAX parser, a client application is required to get information when SAX parser notifies the client application that information is available.

Can we create an XML document using StAX parser?

Java StAX Parser - Create XML Document.


3 Answers

Make sure you read javadocs for Stax: since it is fully streaming parsing mode, only information contained by the current event is available. There are some exceptions, however; getElementText() for example must start at START_ELEMENT, but will then try to combine all textual tokens from inside current element; and when returning, it will point to matching END_ELEMENT.

Conversely, getText() on START_ELEMENT will not returning anything useful (since START_ELEMENT refers to tag, not child text tokens/nodes 'inside' start/end element pair). If you want to use it instead, you have to explicitly move cursor in stream by calling streamReader.next(); whereas getElementText() does it for you.

So what is causing the error? After you have consumed all start/end-element pairs, next token will be END_ELEMENT (matching whatever was the parent tag). So you must check for the case where you get END_ELEMENT, instead of yet another START_ELEMENT.

like image 68
StaxMan Avatar answered Oct 20 '22 08:10

StaxMan


I found a solution that uses XMLEventReader instead of XMLStreamReader:

public MyObject parseXML(String xml)
    throws XMLStreamException, UnsupportedEncodingException
{
    byte[] byteArray = xml.getBytes("UTF-8");
    ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArray);
    XMLInputFactory inputFactory = XMLInputFactory.newInstance();
    XMLEventReader reader = inputFactory.createXMLEventReader(inputStream);

    MyObject object = new MyObject();

    while (reader.hasNext())
    {
        XMLEvent event = (XMLEvent) reader.next();

        if (event.isStartElement())
        {
            StartElement element = event.asStartElement();

            if (element.getName().getLocalPart().equals("ElementOne"))
            {
                event = (XMLEvent) reader.next();

                if (event.isCharacters())
                {
                     String elementOne = event.asCharacters().getData();
                     object.setElementOne(elementOne);
                }
                continue;
            }
            if (element.getName().getLocalPart().equals("ElementTwo"))
            {
                event = (XMLEvent) reader.next();
                if (event.isCharacters())
                {
                     String elementTwo = event.asCharacters().getData();
                     object.setElementTwo(elementTwo);
                }
                continue;
            }
        }
    }

    return object;
}

I would still be interested in seeing a solution using XMLStreamReader.

like image 10
sdoca Avatar answered Oct 20 '22 09:10

sdoca


I faced a similar issue as I was getting "IllegalStateException: Not a textual event" message When I looked through your code I figured out that if you had a condition:

if (event == XMLStreamConstants.START_ELEMENT){
....
addressId = reader.getText(); // it throws exception here
....
}

(Please note: StaXMan did point out this in his answer!)

This happens since to fetch text, XMLStreamReader instance must have encountered 'XMLStreamConstants.CHARACTERS' event!

There maybe a better way to do this...but this is a quick and dirty fix (I have only shown lines of code that may be of interest) Now to make this happen modify your code slightly:

// this will tell the XMLStreamReader that it is appropriate to read the text
boolean pickupText = false

while(reader.hasNext()){

if (event == XMLStreamConstants.START_ELEMENT){
   if( (reader.getLocalName().equals(STATUS) )
   || ( (reader.getLocalName().equals(STATUS) )
   || ((reader.getLocalName().equals(STATUS) ))
         // indicate the reader that it has to pick text soon!
     pickupText = true;
   }
}else if (event == XMLStreamConstants.CHARACTERS){
  String textFromXML = reader.getText();
  // process textFromXML ...

  //...

  //set pickUpText false
  pickupText = false;

 }    

}

Hope that helps!

like image 2
Vikram Avatar answered Oct 20 '22 08:10

Vikram