UnmarshallingXmlAnyElementMixedWithWhitespacesTest.java
/*
* Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.glassfish.jaxb.runtime.unmarshaller;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlAnyElement;
import jakarta.xml.bind.annotation.XmlAttribute;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlElementRef;
import jakarta.xml.bind.annotation.XmlElementRefs;
import jakarta.xml.bind.annotation.XmlMixed;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlValue;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3c.dom.Element;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class UnmarshallingXmlAnyElementMixedWithWhitespacesTest {
private static final String BASIC_BODY_SAMPLE =
"<body><ul><li> <span>l</span></li> <li> <span> l</span></li><li> <span> </span> </li></ul></body>";
private static final String BODY_WITH_NEWLINE_SAMPLE =
"<body><ul><li>\n<span>l</span></li>\n<li>\n<span>\nl</span></li><li>\n<span>\n</span>\n</li></ul></body>";
private static final String DOCUMENT_SAMPLE = "<document> "
+ "<metadata key=\"name\"> testing document</metadata>"
+ " <metadata key=\"timestamp\">123456789</metadata>"
+ " <content>"
+ " <header>header</header> "
+ "<body><ul><li> <span>l</span></li> <li> <span> l</span></li><li> <span> </span> </li></ul></body>"
+ " <footer>footer</footer>"
+ " </content> "
+ "</document>";
private static final String DOCUMENT_EXPECTED = "<document>"
+ "<metadata key=\"name\"> testing document</metadata>"
+ "<metadata key=\"timestamp\">123456789</metadata>"
+ "<content>"
+ "<header>header</header>"
+ "<body><ul><li> <span>l</span></li> <li> <span> l</span></li><li> <span> </span> </li></ul></body>"
+ "<footer>footer</footer>"
+ "</content>"
+ "</document>";
private static final String MIXED_CONTENT_SAMPLE = "<composition>Some text before, " + DOCUMENT_SAMPLE + "\n"
+ " between " + DOCUMENT_SAMPLE + " and after</composition>";
private static final String MIXED_CONTENT_EXPECTED = "<composition>Some text before, " + DOCUMENT_EXPECTED + "\n"
+ " between " + DOCUMENT_EXPECTED + " and after</composition>";
private static JAXBContext context;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
context = JAXBContext.newInstance(MixedContent.class);
}
@Test
public void shouldParseAndSerializeKeepingWhitespacesWithSingleMixed() throws JAXBException {
// given
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(BASIC_BODY_SAMPLE.getBytes(StandardCharsets.UTF_8));
// when
JAXBElement<Body> element = unmarshaller.unmarshal(new StreamSource(inputStream), Body.class);
String actual = serializeObject(element);
// then
assertEquals(BASIC_BODY_SAMPLE, actual);
}
@Test
public void shouldParseAndSerializeKeepingLineBreaksWithSingleMixed() throws JAXBException {
// given
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(BODY_WITH_NEWLINE_SAMPLE.getBytes(StandardCharsets.UTF_8));
// when
JAXBElement<Body> element = unmarshaller.unmarshal(new StreamSource(inputStream), Body.class);
String actual = serializeObject(element);
// then
assertEquals(BODY_WITH_NEWLINE_SAMPLE, actual);
}
@Test
public void shouldParseAndSerializeKeepingWhitespacesOnMixedWithSiblings() throws JAXBException {
// given
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(DOCUMENT_SAMPLE.getBytes(StandardCharsets.UTF_8));
// when
JAXBElement<Document> element = unmarshaller.unmarshal(new StreamSource(inputStream), Document.class);
String actual = serializeObject(element);
// then
assertEquals(DOCUMENT_EXPECTED, actual);
}
@Test
public void shouldParseAndSerializeKeepingWhitespacesOnMixedWithElementRefs() throws JAXBException {
// given
Unmarshaller unmarshaller = context.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(MIXED_CONTENT_SAMPLE.getBytes(StandardCharsets.UTF_8));
// when
JAXBElement<MixedContent> element = unmarshaller.unmarshal(new StreamSource(inputStream), MixedContent.class);
String actual = serializeObject(element);
// then
assertEquals(MIXED_CONTENT_EXPECTED, actual);
assertTrue(element.getValue().elements.get(0) instanceof String);
assertTrue(element.getValue().elements.get(1) instanceof Document);
}
private String serializeObject(JAXBElement<?> element) throws JAXBException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.marshal(element.getValue(), outputStream);
return new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "document")
private static class Document {
@XmlElement
private List<Metadata> metadata;
@XmlElement
private Content content;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "metadata")
private static class Metadata {
@XmlAttribute
private String key;
@XmlValue
private String value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "content")
private static class Content {
@XmlElement
private String header;
@XmlElement
private Body body;
@XmlElement
private String footer;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "body")
private static class Body {
@XmlAnyElement
@XmlMixed
private List<Element> nodes;
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "composition")
private static class MixedContent {
@XmlAnyElement
@XmlElementRefs({@XmlElementRef(name = "document", type = Document.class)})
@XmlMixed
private List<Object> elements;
}
}