11

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();
    }
}
3
  • What does your XML data look like? Are you sure it is well-formed? (Perhaps show us a small amount) Commented Jan 10, 2011 at 22:55
  • The XML is a String response from another class/method that my code will be calling. I'm writing a JUnit to test the parser. I've added the JUnit code to the question, including the method that generates the XML for the test. Commented Jan 10, 2011 at 23:00
  • 1
    my personal approach would be to find a stAX tutorial and make sure it works for me, then extend it to cover your requirements. I note that you only trap START_ELEMENT - I would add an } else { clause that logs the other events. That will tell you how far you have got through the data. Commented Jan 10, 2011 at 23:07

4 Answers 4

11

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.

Sign up to request clarification or add additional context in comments.

Comments

5

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.

Comments

2

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!

Comments

-1

Here is an example with XMLStreamReader:

   XMLInputFactory inputFactory = XMLInputFactory.newInstance();
   Map<String, String> elements = new HashMap<>();

try {
   XMLStreamReader xmlReader = inputFactory.createXMLStreamReader(file);
   String elementValue = "";
   
   while (xmlReader.hasNext()) {
      int xmlEventType = xmlReader.next();
      
      switch (xmlEventType) {  
          // Check for Start Elements
          case XMLStreamConstants.START_ELEMENT:
              
              //Get current Element Name
              String elementName = xmlReader.getLocalName();
              
              if(elementName.equals("td")) {
              //Get Elements Value
              elementValue = xmlReader.getElementText();
              }
              
              //Add the new Start Element to the Map
              elements.put(elementName, elementValue);                
              break;
          default:
             break;
          }    
   }
   //Close Session
   xmlReader.close();        
} catch (Exception e) {
    log.error(e.getMessage(), e);
}

1 Comment

The question was to parse XML from a string not a file. Examples like yours abound out there.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.