如何使 CXF 生成的代码解组为 JAX-B 并忽略未映射的字段?
How to make CXF generated code to unmarshal into JAX-B and ignore unmapped fields?
我调用了一个网络服务,它可以定期在他们的合同中添加一些元素。
示例:SOAP 响应正文包含:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
</picture>
<picture>
<active>false</active>
<code>177</code>
<label>Nikon D5200 Red</label>
<downloadEnabled>true</downloadEnabled>
<extraField>none</extraField>
</picture>
我的 CXF 生成的 JAXB Bean 如下所示:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "pictureListJAXB", propOrder = {
"active",
"code",
"label",
"downloadEnabled",
"extraField"
})
public class PictureListJAXB {
protected boolean active;
protected String code;
protected String label;
protected boolean downloadEnabled;
@XmlElement(nillable = true)
protected String extraField
// And Getters / Setters comes here after
}
JAXB bean 是使用 maven 插件 cxf-codegen-plugin
版本 2.6.2(来自 apache.cxf)生成的。
现在我想知道是否有解决方案可以让我的 Bean 在 SOAP 响应中出现新元素时容错:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
<newUnmappedElement>anything irrelevant</newUnmappedElement>
</picture>
现在,当我收到这样的回复时,我得到了一个 Unmarshalling Error
因为这个新元素。
我的 JAXB 包含我想要管理的最少字段,但我希望 bean 能够处理新元素并忽略它们。
有什么方法可以不重新生成 JAXB Bean 吗?
(现在我必须重新生成 Bean 并发布我的项目)
我检查了 CXF 选项(和 xjc),但在文档(和 google)中没有找到任何内容。解组操作在同样由CXF生成的ReferentialService
中自动完成,那么修改这部分生成的选项就足够了。
这是使用生成的 CXF 调用网络服务的代码 类:
public ReferentialService getReferentialService(String resource, String auth) throws RuntimeException {
// These two classes are generated by CXF
Referential referential;
ReferentialService referentialService;
// Get the resource URL in form http://myserver:port/remote-backend/resource?wsdl
referential = new Referential(new URL(MyConfigUtil.getWSDL(resource)));
referentialService = referential.getReferentialServicePort();
BindingProvider bp = (BindingProvider) referentialService;
// Get the endpoint URL in form http://myserver:port/remote-backend/resource
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, MyConfigUtil.getWebServiceEndPoint(resource));
// Add SOAP credentials
String username = HttpBasicAuthHelper.getUsername(auth);
String password = HttpBasicAuthHelper.getPassword(auth);
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
return referentialService;
}
和通话:
// Throws Exception just for the sample code
public InputStream listPictures(QueryDTO request, String resource, String auth) throws Exception {
InputStream is = null;
if (request != null) {
// This is the WS Call which failed with unmarshal error
List<PictureListJAXB> result = getReferentialService(resource, auth).getPictures(request);
// Some code to convert JAXB into a XML stream
is = convertObjectToXmlStream(result);
}
return is;
}
更新:
我看到 this post,我的感觉是一样的:如果在没有 CXF 的情况下单独使用,JAXB 将忽略未映射的元素。通过使用cxf-codegen-plugin
,情况并非如此。
解决此问题的一种方法是使用 Jaxb 注释 @XmlAnyElement(lax = true) 。这意味着对于该字段,如果元素通过 @XmlRootElement 或 @XmlElementDecl 与 class 关联,则相应 class 的实例将用于填充该字段,如果不是该元素将被设置为 org.w3c.dom.Element.A 示例代码
的实例
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
..
....
@XmlAnyElement(lax = true)
protected List<Object> any;
输入中与 class 的显式属性不对应的任何元素都将被扫入此 list.Checkout more enter link description here
我最终通过查找 this another post 找到了答案,但由于我没有像 post 那样使用声明方式,这让我猜想我应该能够在绑定提供程序。
我对代码的修改是在 BindingProvider
中添加这些属性,就像在 getReferentialService
方法中这样:
bp.getRequestContext().put("schema-validation-enabled", "true");
bp.getRequestContext().put("jaxb-validation-event-handler", new ValidatorHandler());
为了测试,我刚刚创建了一个内部 class ValidatorHandler
:
private class ValidatorHandler extends DefaultValidationEventHandler {
@Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == ValidationEvent.WARNING) {
return super.handleEvent(event);
} else if (event.getSeverity() == ValidationEvent.ERROR
&& event.getMessage().startsWith("unexpected element")) {
return true;
} else {
throw new RuntimeException(event.getMessage()
+ " [line:" + event.getLocator().getLineNumber() + "]");
}
}
}
通过这样做,我不需要修改生成的 bean (JAX-B) 并按默认生成的方式使用它们。
您的回答对我的研究很有帮助。谢谢。
我在 SOAP 响应中遇到了同样的未知元素问题
ValidationEvent.FATAL_ERROR "cvc-complex-type.2.4.a: Invalid content"
我能够添加以下内容
bp.getRequestContext().put("set-jaxb-validation-event-handler", false);
这将关闭仅 JAXB 验证和来自 Danial Kulp CXF 提交者的引用 "For the most part, that is just things like unknown elements or elements in wrong namespaces and such."
我调用了一个网络服务,它可以定期在他们的合同中添加一些元素。
示例:SOAP 响应正文包含:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
</picture>
<picture>
<active>false</active>
<code>177</code>
<label>Nikon D5200 Red</label>
<downloadEnabled>true</downloadEnabled>
<extraField>none</extraField>
</picture>
我的 CXF 生成的 JAXB Bean 如下所示:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "pictureListJAXB", propOrder = {
"active",
"code",
"label",
"downloadEnabled",
"extraField"
})
public class PictureListJAXB {
protected boolean active;
protected String code;
protected String label;
protected boolean downloadEnabled;
@XmlElement(nillable = true)
protected String extraField
// And Getters / Setters comes here after
}
JAXB bean 是使用 maven 插件 cxf-codegen-plugin
版本 2.6.2(来自 apache.cxf)生成的。
现在我想知道是否有解决方案可以让我的 Bean 在 SOAP 响应中出现新元素时容错:
<picture>
<active>true</active>
<code>172</code>
<label>Nikon D3200 Black</label>
<downloadEnabled>true</downloadEnabled>
<newUnmappedElement>anything irrelevant</newUnmappedElement>
</picture>
现在,当我收到这样的回复时,我得到了一个 Unmarshalling Error
因为这个新元素。
我的 JAXB 包含我想要管理的最少字段,但我希望 bean 能够处理新元素并忽略它们。
有什么方法可以不重新生成 JAXB Bean 吗? (现在我必须重新生成 Bean 并发布我的项目)
我检查了 CXF 选项(和 xjc),但在文档(和 google)中没有找到任何内容。解组操作在同样由CXF生成的ReferentialService
中自动完成,那么修改这部分生成的选项就足够了。
这是使用生成的 CXF 调用网络服务的代码 类:
public ReferentialService getReferentialService(String resource, String auth) throws RuntimeException {
// These two classes are generated by CXF
Referential referential;
ReferentialService referentialService;
// Get the resource URL in form http://myserver:port/remote-backend/resource?wsdl
referential = new Referential(new URL(MyConfigUtil.getWSDL(resource)));
referentialService = referential.getReferentialServicePort();
BindingProvider bp = (BindingProvider) referentialService;
// Get the endpoint URL in form http://myserver:port/remote-backend/resource
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, MyConfigUtil.getWebServiceEndPoint(resource));
// Add SOAP credentials
String username = HttpBasicAuthHelper.getUsername(auth);
String password = HttpBasicAuthHelper.getPassword(auth);
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
return referentialService;
}
和通话:
// Throws Exception just for the sample code
public InputStream listPictures(QueryDTO request, String resource, String auth) throws Exception {
InputStream is = null;
if (request != null) {
// This is the WS Call which failed with unmarshal error
List<PictureListJAXB> result = getReferentialService(resource, auth).getPictures(request);
// Some code to convert JAXB into a XML stream
is = convertObjectToXmlStream(result);
}
return is;
}
更新:
我看到 this post,我的感觉是一样的:如果在没有 CXF 的情况下单独使用,JAXB 将忽略未映射的元素。通过使用cxf-codegen-plugin
,情况并非如此。
解决此问题的一种方法是使用 Jaxb 注释 @XmlAnyElement(lax = true) 。这意味着对于该字段,如果元素通过 @XmlRootElement 或 @XmlElementDecl 与 class 关联,则相应 class 的实例将用于填充该字段,如果不是该元素将被设置为 org.w3c.dom.Element.A 示例代码
的实例@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
..
....
@XmlAnyElement(lax = true)
protected List<Object> any;
输入中与 class 的显式属性不对应的任何元素都将被扫入此 list.Checkout more enter link description here
我最终通过查找 this another post 找到了答案,但由于我没有像 post 那样使用声明方式,这让我猜想我应该能够在绑定提供程序。
我对代码的修改是在 BindingProvider
中添加这些属性,就像在 getReferentialService
方法中这样:
bp.getRequestContext().put("schema-validation-enabled", "true");
bp.getRequestContext().put("jaxb-validation-event-handler", new ValidatorHandler());
为了测试,我刚刚创建了一个内部 class ValidatorHandler
:
private class ValidatorHandler extends DefaultValidationEventHandler {
@Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == ValidationEvent.WARNING) {
return super.handleEvent(event);
} else if (event.getSeverity() == ValidationEvent.ERROR
&& event.getMessage().startsWith("unexpected element")) {
return true;
} else {
throw new RuntimeException(event.getMessage()
+ " [line:" + event.getLocator().getLineNumber() + "]");
}
}
}
通过这样做,我不需要修改生成的 bean (JAX-B) 并按默认生成的方式使用它们。
您的回答对我的研究很有帮助。谢谢。 我在 SOAP 响应中遇到了同样的未知元素问题
ValidationEvent.FATAL_ERROR "cvc-complex-type.2.4.a: Invalid content"
我能够添加以下内容
bp.getRequestContext().put("set-jaxb-validation-event-handler", false);
这将关闭仅 JAXB 验证和来自 Danial Kulp CXF 提交者的引用 "For the most part, that is just things like unknown elements or elements in wrong namespaces and such."