3

我调用了一个网络服务,它可以定期在他们的合同中添加一些元素。

示例: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),但在文档(和谷歌)中一无所获。解组操作也是在ReferentialServiceCXF 生成的中自动完成的,然后一个选项来修改这部分的生成就足够了。

以下是使用 CXF 生成的类调用 Web 服务的代码:

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;
}

更新:我看到了这篇文章,我的感觉是一样的:如果在没有 CXF 的情况下单独使用,JAXB 将忽略未映射的元素。通过使用cxf-codegen-plugin,情况并非如此。

4

3 回答 3

4

我终于通过查看另一个帖子找到了答案,但由于我没有使用帖子中的声明方式,这让我猜想我应该能够在绑定提供程序上添加一些属性。

我对代码的修改是BindingProvider在方法中像这样添加这些属性getReferentialService

bp.getRequestContext().put("schema-validation-enabled", "true");
bp.getRequestContext().put("jaxb-validation-event-handler", new ValidatorHandler());

为了测试,我刚刚创建了一个内部类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) 并使用它们,因为它们是默认生成的。

于 2015-07-07T16:00:28.363 回答
2

解决此问题的一种方法是使用 Jaxb 注释 @XmlAnyElement(lax = true) 。这意味着对于该字段,如果元素通过 @XmlRootElement 或 @XmlElementDecl 与类相关联,则将使用相应类的实例如果不是,则填充字段元素将被设置为 org.w3c.dom.Element.A 示例代码的实例

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
    ..
    ....
    @XmlAnyElement(lax = true)
    protected List<Object> any;

输入中与类的显式属性不对应的任何元素都将被扫入此列表。查看更多在此处输入链接描述

于 2015-06-27T08:37:07.153 回答
0

你的回答对我的研究很有帮助。谢谢。我在 SOAP 响应中遇到了同样的未知元素问题

ValidationEvent.FATAL_ERROR "cvc-complex-type.2.4.a: 无效内容"

我能够添加以下内容

bp.getRequestContext().put("set-jaxb-validation-event-handler", false);

这将关闭仅 JAXB 验证和来自 Danial Kulp CXF 提交者的引用“在大多数情况下,这只是诸如未知元素或错误名称空间中的元素之类的东西。”

于 2020-05-03T18:08:19.163 回答