使用xs:any和混合内容在JAXB中维护空格

时间:2014-07-22 18:05:17

标签: java jaxb unmarshalling

我有一个带有xs:any元素的模式。此元素可能包含具有混合内容的其他元素。我试图使用JAXB将其解组为Java对象(使用' any'作为元素)。

来自架构:

<xs:element name="a">
    <xs:complexType>
        <xs:sequence>
            <xs:any processContents="lax"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

一般来说,这是有效的。但是当处理具有混合内容的元素时,嵌套节点之间的空白将丢失。

的test.xml:

<a><foo><b>Hello</b> <i>World</i></foo></a>

像这样解组:

JAXBContext jc = JAXBContext.newInstance(A.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = this.getClass().getResourceAsStream("/data/test.xml");
A a = (A) unmarshaller.unmarshal(inputStream);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(a, System.out);

结果如下:

<a><foo><b>Hello</b><i>World</i></foo></a>

我丢失了<foo>元素的子标记之间的空格。我确定这是将空白带到这里的unmarshal步骤,但我确实需要它来实现往返。

请注意,它只删除了仅限空白的文本内容。这可以按预期工作:

<a><foo><b>Hello</b> to you <i>World</i></foo></a>

我尝试添加xml:space="preserve"(例如,参见JAXB: How to keep consecutive spaces as they are in source XML during unmarshalling),但这对元素之间的空白没有影响。我已经尝试将processContents设置为strictlaxskip,但没有一个帮助过。

1 个答案:

答案 0 :(得分:0)

面对类似的问题后,我可以提出以下解决方案(对于这种特定情况,对于某些其他复杂的XML结构而言,它并不完美)。

package com.stackoverflow.answers;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.transform.stream.StreamSource;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.w3c.dom.Element;

public class XmlAnyElementWithWhiteSpacesTest {

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlRootElement(name = "a")
    private static class A {

        @XmlAnyElement
        @XmlMixed
        private List<Element> elements;
    }

    private static final String SAMPLE = "<a><foo><b>Hello</b> <i>World</i></foo></a>";

    @Test
    public void shouldParseAndSerializeKeepingWhiteSpaceElements() throws JAXBException {
        // given
        JAXBContext jc = JAXBContext.newInstance(A.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        InputStream inputStream = new ByteArrayInputStream(SAMPLE.getBytes(StandardCharsets.UTF_8));
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        // when
        JAXBElement<A> a = unmarshaller.unmarshal(new StreamSource(inputStream), A.class);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        marshaller.marshal(a.getValue(), outputStream);
        String actual = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
        // then
        assertEquals(SAMPLE, actual);
    }

}

关键点是:

  • 使用@XmlMixed批注
  • StreamSource的使用方式

您可以将List<Object>List<Element>用于“ XML任意内容”属性。