Skip to content

Commit

Permalink
Support marshalling of JAXBElements in REST Client and Server Reactive
Browse files Browse the repository at this point in the history
I made also this change in the server, even though I think it's a niche use case here, but just to keep the server and client writers synced. 

Also, I discarded the idea of returning JAXBElements which would be a more complex scenario. So, I updated both readers in the client and in the server to not support the JAXBElement class. 

Fix #33875
  • Loading branch information
Sgitario committed Jun 9, 2023
1 parent 44cea45 commit 6043c57
Showing 6 changed files with 87 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@

import java.io.StringWriter;

import javax.xml.namespace.QName;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.ws.rs.Consumes;
@@ -18,6 +20,7 @@
import jakarta.ws.rs.container.Suspended;
import jakarta.ws.rs.core.MediaType;
import jakarta.xml.bind.JAXB;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;
@@ -178,6 +181,19 @@ public void testResourceUsingModelWithSameName() {
.body(is("bb"));
}

@Test
public void testSupportReturningJaxbElement() {
Person response = RestAssured
.get("/simple/person-as-jaxb-element")
.then()
.statusCode(200)
.contentType(ContentType.XML)
.extract().as(Person.class);

assertEquals("Bob", response.getFirst());
assertEquals("Builder", response.getLast());
}

private String toXml(Object person) {
StringWriter sw = new StringWriter();
JAXB.marshal(person, sw);
@@ -246,6 +262,13 @@ public Person getPerson() {
return person;
}

@GET
@Produces(MediaType.APPLICATION_XML)
@Path("/person-as-jaxb-element")
public JAXBElement<Person> getPersonAsJaxbElement() {
return new JAXBElement<>(new QName("person"), Person.class, getPerson());
}

@POST
@Path("/person")
@Produces(MediaType.APPLICATION_XML)
Original file line number Diff line number Diff line change
@@ -62,6 +62,9 @@ protected boolean isReadable(MediaType mediaType, Class<?> type) {
if (String.class.equals(type)) { // don't attempt to read plain strings
return false;
}
if (JAXBElement.class.equals(type)) { // don't attempt to read JAXB elements
return false;
}
String subtype = mediaType.getSubtype();
boolean isCorrectMediaType = "application".equals(mediaType.getType()) || "text".equals(mediaType.getType());
return (isCorrectMediaType && "xml".equalsIgnoreCase(subtype) || subtype.endsWith("+xml"))
Original file line number Diff line number Diff line change
@@ -55,11 +55,15 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte

protected void marshal(Object o, OutputStream outputStream) {
try {
Object jaxbObject = o;
Class<?> clazz = o.getClass();
XmlRootElement jaxbElement = clazz.getAnnotation(XmlRootElement.class);
if (jaxbElement == null) {
jaxbObject = new JAXBElement(new QName(Introspector.decapitalize(clazz.getSimpleName())), clazz, o);
Object jaxbObject = o;
if (o instanceof JAXBElement) {
clazz = ((JAXBElement<?>) o).getDeclaredType();
} else {
XmlRootElement jaxbElement = clazz.getAnnotation(XmlRootElement.class);
if (jaxbElement == null) {
jaxbObject = new JAXBElement(new QName(Introspector.decapitalize(clazz.getSimpleName())), clazz, o);
}
}

getMarshall(clazz).marshal(jaxbObject, outputStream);
Original file line number Diff line number Diff line change
@@ -5,12 +5,18 @@
import java.net.URI;
import java.util.Objects;

import javax.xml.namespace.QName;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.annotation.XmlRootElement;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

@@ -26,18 +32,25 @@ public class SimpleJaxbTest {
@TestHTTPResource
URI uri;

XmlClient client;

@BeforeEach
public void setup() {
client = QuarkusRestClientBuilder.newBuilder().baseUri(uri).build(XmlClient.class);
}

@Test
void shouldConsumeXMLEntity() {
var dto = QuarkusRestClientBuilder.newBuilder().baseUri(uri).build(XmlClient.class)
.dto();
assertThat(dto).isEqualTo(new Dto("foo", "bar"));
// assertThat(client.dto()).isEqualTo(new Dto("foo", "bar"));
assertThat(client.dtoAsJaxbElement().getValue()).isEqualTo(new Dto("foo", "bar"));
assertThat(client.createDto(new Dto("foo", "bar"))).isEqualTo(new Dto("foo", "bar"));
assertThat(client.createDto(new JAXBElement<>(new QName("Dto"), Dto.class, new Dto("foo", "bar"))))
.isEqualTo(new Dto("foo", "bar"));
}

@Test
void shouldConsumePlainXMLEntity() {
var dto = QuarkusRestClientBuilder.newBuilder().baseUri(uri).build(XmlClient.class)
.plain();
assertThat(dto).isEqualTo(new Dto("foo", "bar"));
assertThat(client.plain()).isEqualTo(new Dto("foo", "bar"));
}

@Path("/xml")
@@ -48,6 +61,23 @@ public interface XmlClient {
@Produces(MediaType.APPLICATION_XML)
Dto dto();

@GET
@Path("/dto")
@Produces(MediaType.APPLICATION_XML)
JAXBElement<Dto> dtoAsJaxbElement();

@POST
@Path("/dto")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
Dto createDto(Dto dto);

@POST
@Path("/dto")
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
Dto createDto(JAXBElement<Dto> dto);

@GET
@Path("/plain")
@Produces(MediaType.TEXT_XML)
@@ -67,6 +97,14 @@ public String dto() {
return DTO_FOO_BAR;
}

@POST
@Consumes(MediaType.APPLICATION_XML)
@Produces(MediaType.APPLICATION_XML)
@Path("/dto")
public String dto(String dto) {
return dto;
}

@GET
@Produces(MediaType.TEXT_XML)
@Path("/plain")
Original file line number Diff line number Diff line change
@@ -43,6 +43,9 @@ protected boolean isReadable(MediaType mediaType, Class<?> type) {
if (String.class.equals(type)) { // don't attempt to read plain strings
return false;
}
if (JAXBElement.class.equals(type)) { // don't attempt to read JAXB elements
return false;
}
String subtype = mediaType.getSubtype();
boolean isCorrectMediaType = "application".equals(mediaType.getType()) || "text".equals(mediaType.getType());
return (isCorrectMediaType && "xml".equalsIgnoreCase(subtype) || subtype.endsWith("+xml"))
Original file line number Diff line number Diff line change
@@ -46,11 +46,13 @@ private void setContentTypeIfNecessary(MultivaluedMap<String, Object> httpHeader

protected void marshal(Object o, OutputStream outputStream) {
try {
Object jaxbObject = o;
Class<?> clazz = o.getClass();
XmlRootElement jaxbElement = clazz.getAnnotation(XmlRootElement.class);
if (jaxbElement == null) {
jaxbObject = new JAXBElement(new QName(Introspector.decapitalize(clazz.getSimpleName())), clazz, o);
Object jaxbObject = o;
if (!(o instanceof JAXBElement)) {
XmlRootElement jaxbElement = clazz.getAnnotation(XmlRootElement.class);
if (jaxbElement == null) {
jaxbObject = new JAXBElement(new QName(Introspector.decapitalize(clazz.getSimpleName())), clazz, o);
}
}

marshaller.marshal(jaxbObject, outputStream);

0 comments on commit 6043c57

Please sign in to comment.