【问题标题】:Unmashalling with StAX - it skips elements if there's no whitespace between them使用 StAX 解组 - 如果元素之间没有空格,它会跳过元素
【发布时间】:2015-06-23 20:55:50
【问题描述】:

上下文

我需要解析一个 XML。这个 XML 很大,所以我使用 StAx 来处理我感兴趣的每个元素。我使用JDK自带的默认实现。

问题

当一个 XML 元素在另一个相同类型的元素之前(例如 <person>)并且它们之间没有任何字符,它会跳过第二个。所以如果我一个接一个有10个,我只能解组5个人。例如:

<people><person>..</person><person>..</person></people>

我针对封装在方法countUnmarshalledPersonEntities() 中的一段代码构建了一个测试来显示此行为。

问题是,当元素之间有空格时:

<people><person><id>1</id></person> <person><id>2</id></person></people>

它解组两个实体,没关系。

但是当节点之间没有空格时:

<people><person><id>1</id></person><person><id>2</id></person></people>

第一次解组跳过下一个打开标签&lt;person&gt;,然后忽略第二个人。我只解析 1 个实体。

测试

package org.opensource.lab.stream;

import static org.junit.Assert.assertEquals;

import java.io.InputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.IOUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class StreamParserProblemTest {
    private XMLInputFactory xmlif;
    private XMLStreamReader xmlStreamReader;
    private Unmarshaller personUnmarshaller;

    private final InputStream xmlStreamPersonsNoSeparated = IOUtils.toInputStream(
            "<people><person><id>1</id></person><person><id>2</id></person></people>"
            );
    private final InputStream xmlStreamWithPersonsWhitespaceSeparated = IOUtils.toInputStream(
            "<people><person><id>1</id></person> <person><id>2</id></person></people>"
            );

    @Before
    public void setUp() throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
        personUnmarshaller = jaxbContext.createUnmarshaller();
        xmlif = XMLInputFactory.newInstance();
    }

    @After
    public void cleanUp() throws Exception {
        if(xmlStreamReader != null) {
            xmlStreamReader.close();
        }
    }

    @XmlRootElement(name = "person")
    static class Person {
        String id;
    }

    @Test
    public void whenNoSpacesBetweenNodes_shouldFind2Persons_FAIL() throws Exception {
        xmlStreamReader = xmlif.createXMLStreamReader(xmlStreamPersonsNoSeparated, "UTF-8");

        int personTagsFound = countUnmarshalledPersonEntities();

        assertEquals(personTagsFound, 2);
    }

    /**
     * I don't know why, but if there's at least one whitespace character between node of the same type it won't skip.
     * 
     * @throws Exception in a test
     */
    @Test
    public void whenWithSpacesBetweenNodes_shouldFind2Persons_SUCCESS() throws Exception {
        xmlStreamReader = xmlif.createXMLStreamReader(xmlStreamWithPersonsWhitespaceSeparated, "UTF-8");

        int personTagsFound = countUnmarshalledPersonEntities();

        assertEquals(personTagsFound, 2);
    }

    /**
     * CODE to test.
     * 
     * @return number of unmarshalled persons (people).
     * @throws Exception
     */
    private int countUnmarshalledPersonEntities() throws Exception {
        int personTagsFound = 0;

        while (xmlStreamReader.hasNext()) {
            int type = xmlStreamReader.next();

            if (type == XMLStreamConstants.START_ELEMENT && xmlStreamReader.getName().toString().equalsIgnoreCase("person")) {
                personUnmarshaller.unmarshal(xmlStreamReader, Person.class);
                personTagsFound++;
            }
        }

        return personTagsFound;
    }
}

你知道代码有什么问题吗?

谢谢。

【问题讨论】:

    标签: java xml streaming unmarshalling stax


    【解决方案1】:

    感谢您附加的单元测试,这确实让您更容易理解!

    当您对xmlStreamReader 执行unmarshal 时,只要存在属于您的实体的标签,XMLStreamReader 就会自行隐式调用next。因此,在您关闭 person 标记之后,它将调用 next 并指向下一个实体的第一个 person 标记。在下一次迭代中调用xmlStreamReader.next(),您将跳过它。如果您的实体之间存在空格,则不会发生这种情况,因为在解析后,您的阅读器会指向空格。

    这个修改后的代码对我有用,你的两个单元测试都成功了:

        while (xmlStreamReader.hasNext()) {
            if (xmlStreamReader.isStartElement() && xmlStreamReader.getName().toString().equalsIgnoreCase("person")) {
                personUnmarshaller.unmarshal(xmlStreamReader, Person.class);
                personTagsFound++;
            } else {
                xmlStreamReader.next();
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多