Quarkus返回XML响应时类XML类型名冲突问题求解
Quarkus + CXF 下JAXB冲突导致XML响应错误及多余命名空间问题
环境
- Quarkus版本:3.2.0.Final
- Quarkus CXF版本:2.2.2(也曾尝试5.2.0)
场景代码
REST资源类
@GET @Produces(MediaType.APPLICATION_XML) public MyResponse<MyEntity> serviceThatReturnsXml() { // 返回MyResponse实例 }
响应实体类
@Data @NoArgsConstructor @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "TagName") public class MyResponse<T> { @XmlElement(name = "ChildTagName") private List<T> items; }
问题描述
调用该REST接口时抛出JAXB冲突错误:
Two classes have the same XML type name "{http://www.w3.org/2005/08/addressing}EndpointReferenceType". Use @XmlType.name and @XmlType.namespace to assign different names to them. this problem is related to the following location: at jakarta.xml.ws.wsaddressing.W3CEndpointReference this problem is related to the following location: at org.apache.cxf.ws.addressing.EndpointReferenceType at public org.apache.cxf.ws.addressing.EndpointReferenceType org.apache.cxf.ws.addressing.ObjectFactory.createEndpointReferenceType() at org.apache.cxf.ws.addressing.ObjectFactory at protected java.util.List org.apache.cxf.ws.addressing.MetadataType.any at org.apache.cxf.ws.addressing.MetadataType at io.quarkus.jaxb.runtime.JaxbContextProducer.createJAXBContext(JaxbContextProducer.java:82) at io.quarkus.jaxb.runtime.JaxbContextProducer_ClientProxy.createJAXBContext(Unknown Source) at io.quarkus.resteasy.reactive.jaxb.runtime.JAXBContextContextResolver.getContext(JAXBContextContextResolver.java:27) at io.quarkus.resteasy.reactive.jaxb.runtime.JAXBContextContextResolver.getContext(JAXBContextContextResolver.java:13) at io.quarkus.resteasy.reactive.jaxb.runtime.serialisers.ServerJaxbMessageBodyWriter.getMarshall(ServerJaxbMessageBodyWriter.java:82) at io.quarkus.resteasy.reactive.jaxb.runtime.serialisers.ServerJaxbMessageBodyWriter.marshal(ServerJaxbMessageBodyWriter.java:69) at io.quarkus.resteasy.reactive.jaxb.runtime.serialisers.ServerJaxbMessageBodyWriter.writeResponse(ServerJaxbMessageBodyWriter.java:51) at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:227) at org.jboss.resteasy.reactive.server.core.ServerSerialisers.invokeWriter(ServerSerialisers.java:195) at org.jboss.resteasy.reactive.server.core.serialization.FixedEntityWriter.write(FixedEntityWriter.java:28) at org.jboss.resteasy.reactive.server.handlers.ResponseWriterHandler.handle(ResponseWriterHandler.java:34) at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:147) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145) at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:833)
当前POM依赖:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jackson</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-resteasy-reactive-jaxb</artifactId> </dependency> <dependency> <groupId>jakarta.xml.ws</groupId> <artifactId>jakarta.xml.ws-api</artifactId> </dependency> <dependency> <groupId>jakarta.jws</groupId> <artifactId>jakarta.jws-api</artifactId> </dependency> <dependency> <groupId>io.quarkiverse.cxf</groupId> <artifactId>quarkus-cxf</artifactId> </dependency>
已尝试的方案
- 配置排除CXF的
EndpointReferenceType:
quarkus: jaxb: exclude-classes: org.apache.cxf.ws.addressing.EndpointReferenceType validate-jaxb-context: false
→ 无效,错误依旧。
- 配置排除Jakarta的
W3CEndpointReference:
quarkus: jaxb: exclude-classes: jakarta.xml.ws.wsaddressing.W3CEndpointReference validate-jaxb-context: false
→ 错误消失,但返回的XML根标签附带大量未使用的命名空间:
<TagName xmlns:ns2="http://java.sun.com/xml/ns/javaee" xmlns:ns3="http://api.nef.co.adapter.model.esb.p4.org" xmlns:ns4="http://www.w3.org/2006/07/ws-policy" xmlns:ns5="http://www.w3.org/2006/05/addressing/wsdl" xmlns:ns6="http://cxf.apache.org/configuration/security" xmlns:ns7="https://jakarta.ee/xml/ns/jakartaee" xmlns:ns8="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:ns9="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:ns10="http://www.w3.org/2005/08/addressing" xmlns:ns11="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:ns12="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ns13="http://schemas.xmlsoap.org/wsdl/http/" xmlns:ns14="http://base.api.nef.co.adapter.model.esb.p4.org" xmlns:ns15="http://esb.p4.org" xmlns:ns16="http://cxf.apache.org/transports/http/configuration" xmlns:ns17="http://cxf.apache.org/bindings/xformat"/>
需求
CXF扩展无法移除(其他代码依赖),需要解决依赖冲突、消除错误同时去掉多余的XML命名空间。
解决方案
1. 精准控制JAXB上下文的类范围
默认情况下Quarkus的JAXB会扫描所有带JAXB注解的类,包括CXF的内部类,导致冲突。可以通过显式指定JAXB需要处理的类来缩小范围:
quarkus: jaxb: include-classes: com.yourpackage.MyResponse, com.yourpackage.MyEntity validate-jaxb-context: true
替换com.yourpackage为你的实体类实际包名,只包含自己需要序列化的类,彻底排除CXF的JAXB类参与上下文创建。
2. 调整依赖,避免重复的WS-Addressing类
CXF本身已经包含了WS-Addressing的实现,而jakarta.xml.ws-api是API包,可能存在重复定义。可以尝试在POM中排除jakarta.xml.ws-api:
<dependency> <groupId>io.quarkiverse.cxf</groupId> <artifactId>quarkus-cxf</artifactId> <exclusions> <exclusion> <groupId>jakarta.xml.ws</groupId> <artifactId>jakarta.xml.ws-api</artifactId> </exclusion> </exclusions> </dependency>
若其他代码直接依赖jakarta.xml.ws-api的类,可改用Quarkus提供的CXF集成依赖替代手动引入API包。
3. 自定义JAXBContextResolver
如果配置不生效,编写自定义的JAXBContextResolver,手动创建只包含自己实体类的JAXB上下文:
import jakarta.ws.rs.ext.ContextResolver; import jakarta.ws.rs.ext.Provider; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; @Provider public class CustomJAXBContextResolver implements ContextResolver<JAXBContext> { private final JAXBContext context; public CustomJAXBContextResolver() throws JAXBException { // 仅包含需要序列化的实体类 this.context = JAXBContext.newInstance(MyResponse.class, MyEntity.class); } @Override public JAXBContext getContext(Class<?> type) { if (type == MyResponse.class || type == MyEntity.class) { return context; } return null; } }
这个类会被Resteasy自动识别,为你的实体类提供专属的JAXB上下文,完全避开CXF的类。
4. 去除多余命名空间
在实体类上显式指定空命名空间:
@Data @NoArgsConstructor @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "TagName", namespace = "") @XmlType(namespace = "") public class MyResponse<T> { @XmlElement(name = "ChildTagName", namespace = "") private List<T> items; }
也可以通过自定义MessageBodyWriter接管XML序列化过程,进一步控制命名空间生成。
内容的提问来源于stack exchange,提问作者Politechniczny
相关产品推荐
相关产品推荐

