XML文字列を解析するために初めてstaxを使用しています。いくつかの例を見つけましたが、コードを機能させることができません。これは私のコードの最新バージョンです:
_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);
}
}
}
_
「event」と「reader.getElementText()」に時計を置きました。コードが停止したとき
_String element = reader.getLocalName();
_
「reader.getElementText()」の値が表示されますが、その行から離れるとすぐに評価できなくなります。コードが停止したとき:
_status = reader.getElementText();
_
「エレメント」ウォッチは正しい値を表示します。最後に、コードをもう1行ステップすると、次の例外が発生します。
_(com.ctc.wstx.exc.WstxParsingException) com.ctc.wstx.exc.WstxParsingException: Current state not START_ELEMENT
at [row,col {unknown-source}]: [1,29]
_
代わりにstatus = reader.getText();
を使用しようとしましたが、次の例外が発生します。
_(Java.lang.IllegalStateException) Java.lang.IllegalStateException: Not a textual event (END_ELEMENT)
_
誰かが私が間違っていることを指摘できますか?
編集:
テストに使用するJUnitコードの追加:
_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();
}
}
_
XMLStreamReaderの代わりにXMLEventReaderを使用するソリューションを見つけました。
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;
}
XMLStreamReaderを使用したソリューションを見たいと思っています。
Staxのjavadocsを必ずお読みください。これは完全なストリーミング解析モードであるため、現在のイベントに含まれる情報のみが利用可能です。ただし、いくつかの例外があります。たとえば、getElementText()はSTART_ELEMENTで開始する必要がありますが、現在の要素内からすべてのテキストトークンを結合しようとします。戻ると、一致するEND_ELEMENTを指します。
逆に、START_ELEMENTのgetText()は有用なものを返しません(START_ELEMENTはタグを参照し、開始/終了要素のペア内の子テキストトークン/ノードではないため)。代わりに使用する場合は、streamReader.next();を呼び出して、カーソルをストリーム内で明示的に移動する必要があります。一方、getElementText()はそれを行います。
では、何がエラーの原因ですか?すべての開始/終了要素のペアを消費した後、次のトークンはEND_ELEMENTになります(親タグが何であれ一致します)。したがって、さらに別のSTART_ELEMENTではなく、END_ELEMENTを取得する場合を確認する必要があります。
「IllegalStateException:テキストイベントではありません」というメッセージが表示されたときと同様の問題に直面しました。コードを調べたところ、次のような条件があることがわかりました。
if (event == XMLStreamConstants.START_ELEMENT){
....
addressId = reader.getText(); // it throws exception here
....
}
(注意:StaXManは彼の答えでこれを指摘しました!)
これは、テキストをフェッチするために、XMLStreamReaderインスタンスが「XMLStreamConstants.CHARACTERS」イベントを検出した必要があるために発生します。
これを行うためのより良い方法があるかもしれません...しかし、これは迅速で汚い修正です(興味があるかもしれないコード行だけを示しました)これを実現するには、コードを少し変更します。
// 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;
}
}
お役に立てば幸いです。
XMLStreamReaderの例を次に示します。
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
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);
}