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

问题描述 投票:1回答:1

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

从架构:

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

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

test.hml:

<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>元素的子标签之间的空间。我确定这是将空白带到这里的解散步骤,但我确实需要它才能在往返中幸存下来。

请注意,它只删除了仅限空格的文本内容。这符合要求:

<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,但没有一个帮助过。

java jaxb unmarshalling
1个回答
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 any content”属性。

© www.soinside.com 2019 - 2024. All rights reserved.