Repository: quarkusio/quarkus
Issue: quarkus-rest-client-reactive-jaxb does not support direct marshalling of JAXBElement<T>

### Describe the bug

After writing the below explanation, I'm unsure if this should have been filed as a _Feature request_ or I'm just pointing out an implicit inconsistency in the support of JAXB. But anyway:

I'm doing a port of an old SB/camel service and I'm defining a REST client interface and need XML serialisation support. My "Xml" classes are generated with `xsdtojava` from a couple of xsd-files. I'm providing my own `JAXBContext` like described in this [guide](https://quarkus.io/guides/resteasy-reactive#customize-the-jaxb-configuration).

Because I'm just using the supplied xsd-files, my request model object is is in the form of `JAXBElement<eu.request.SomeType>`. I would have assumed that defining a method like this

```java
    @POST
    @Produces(MediaType.APPLICATION_XML)
    @Consumes(MediaType.APPLICATION_XML)
    eu.response.Response getVehicleByRegno(JAXBElement<eu.request.SomeType> request)
```

that the http-client would correctly serialize this xml element: 
```
JAXBElement<eu.request.SomeType> req = objectFactory.createKehys(messageType);
```

as

```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<kehys>
...
</kehys>
```

However the element is marshalled as
 
```xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JAXBElement>
<kehys>
...
</kehys>
</JAXBElement>
```

After looking into the [implementation](https://github.com/quarkusio/quarkus/blob/main/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java)  of JAXB/Xml `MessageBodyWriter` I can see that only classes annotated with `XmlRootElement` are supported (the [server](https://github.com/quarkusio/quarkus/blob/main/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java) version has the same limitation). If not annotated, the object in question is wrapped in a JAXBElement ([client implementation](https://github.com/quarkusio/quarkus/blob/main/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java#L49-L54)):

```java
            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);
            }
```

This explains the extra `<JAXBElement>` in my generated xml above.

So my question or issue: 

Now that I can define my own JAXBContext that may include classes not annotated with `XmlRootElement`, shouldn't I be able to serialise them as well? 😃 

(As I'm porting a Camel service I checked how Camel checks if a Object is serialisable: [Camel](https://github.com/apache/camel/blob/main/components/camel-jaxb/src/main/java/org/apache/camel/converter/jaxb/JaxbDataFormat.java#L204) uses `JAXBIntrospector.isElement(element)`)


### Expected behavior

Being able to generate XML based on classes in a provided JAXBContext.

### Actual behavior

Only classes annotated with `XmlRootElement` are probably supported. This is could be seen as "inconsistent" with the ability to provide your own JAXBContext.

### How to Reproduce?

If needed, I will provide a project that demonstrate the issue, but until then I think the explanation above sufficiently explains the issue.

### Output of `uname -a` or `ver`

_No response_

### Output of `java -version`

openjdk version "17.0.5" 2022-10-18 OpenJDK Runtime Environment Temurin-17.0.5+8 (build 17.0.5+8) OpenJDK 64-Bit Server VM Temurin-17.0.5+8 (build 17.0.5+8, mixed mode, sharing)

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

2.16.2.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

_No response_

### Additional information

_No response_

Changed files:
extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java
extensions/resteasy-reactive/rest-client-reactive-jaxb/deployment/src/test/java/io/quarkus/rest/client/reactive/jaxb/test/SimpleJaxbTest.java
extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/SimpleXmlTest.java
extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java

diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/SimpleXmlTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/SimpleXmlTest.java
index 214d074bad7..4cfe409be76 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/SimpleXmlTest.java
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/deployment/src/test/java/io/quarkus/resteasy/reactive/jaxb/deployment/test/SimpleXmlTest.java
@@ -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)
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java
index b5a91eec309..924e8113ec0 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jaxb/runtime/src/main/java/io/quarkus/resteasy/reactive/jaxb/runtime/serialisers/ServerJaxbMessageBodyWriter.java
@@ -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);
diff --git a/extensions/resteasy-reactive/rest-client-reactive-jaxb/deployment/src/test/java/io/quarkus/rest/client/reactive/jaxb/test/SimpleJaxbTest.java b/extensions/resteasy-reactive/rest-client-reactive-jaxb/deployment/src/test/java/io/quarkus/rest/client/reactive/jaxb/test/SimpleJaxbTest.java
index 4efea63bf3f..2897be131bb 100644
--- a/extensions/resteasy-reactive/rest-client-reactive-jaxb/deployment/src/test/java/io/quarkus/rest/client/reactive/jaxb/test/SimpleJaxbTest.java
+++ b/extensions/resteasy-reactive/rest-client-reactive-jaxb/deployment/src/test/java/io/quarkus/rest/client/reactive/jaxb/test/SimpleJaxbTest.java
@@ -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,24 @@ 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.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 +60,18 @@ public interface XmlClient {
         @Produces(MediaType.APPLICATION_XML)
         Dto dto();
 
+        @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 +91,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")
diff --git a/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java b/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java
index 9d1b1f90438..5cb016a42b5 100644
--- a/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java
+++ b/extensions/resteasy-reactive/rest-client-reactive-jaxb/runtime/src/main/java/io/quarkus/rest/client/reactive/jaxb/runtime/ClientMessageBodyWriter.java
@@ -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);