JAXB空元素反序列化

5
问题如下:
我得到的soap响应中有空元素(例如... <someDate /> ...),因此当JAXB想要解析此元素而不是使用null值设置适当字段时,会抛出异常。
如何配置JAXB将空元素视为null? 我们能否仅使用JAXB来实现这一点(而不使用某些第三方解决方案)?

1
你能分享更多的信息吗?异常堆栈跟一些代码可以吗? - W Almir
1个回答

11

基础问题

空的String不是xsd:date类型的有效值。为了符合XML模式,可选元素应该表示为不存在的节点。


为什么基础问题会影响您

所有JAXB实现都将识别空的String不是xsd:date的有效值。它们通过向ValidationEventHandler实例报告来实现这一点。您可以通过执行以下操作自行查看:

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    unmarshaller.setEventHandler(new ValidationEventHandler() {

        @Override
        public boolean handleEvent(ValidationEvent event) {
            System.out.println(event);
            return true;
        }
    });

您正在使用的 JAX-WS 实现利用 EclipseLink MOXy 作为 JAXB 提供程序。在您使用的版本中,MOXy 将默认抛出异常,当遇到严重性为 ERROR 的 ValidationEvent 时,而不是像参考实现那样是 FATAL_ERROR。这已经在以下错误中得到了修正:

解决方法

如果您正在直接使用JAXB API,可以简单地覆盖默认的ValidationEventHandler。在JAX-WS环境中,可以使用XmlAdapter提供自定义转换逻辑。我们将利用一个XmlAdapter来覆盖如何处理对/从Date的转换。

XmlAdapter(DateAdapter)

import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<String, Date>{

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public Date unmarshal(String v) throws Exception {
        if(v.length() == 0) {
            return null;
        }
        return dateFormat.parse(v);
    }

    @Override
    public String marshal(Date v) throws Exception {
        if(null == v) {
            return null;
        }
        return dateFormat.format(v);
    }

}

Java模型(根)

使用@XmlJavaTypeAdapter注解来引用XmlAdapter。如果您希望这个XmlAdapter适用于所有的Date实例,您可以在包级别上注册它(请参见:http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html)。

import java.util.Date;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlSchemaType(name = "date")
    @XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class)
    private Date abc;

    @XmlSchemaType(name="date")
    @XmlJavaTypeAdapter(value=DateAdapter.class, type=Date.class)
    private Date qwe;

}

演示代码

以下是一个独立的示例,您可以运行以查看所有内容是否正常工作。

jaxb.properties

要在独立示例中使用MOXy作为您的JAXB提供程序,您需要在与域模型相同的包中包含一个名为jaxb.propeties的文件,并具有以下条目(请参见:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)。
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

input.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <abc></abc>
    <qwe>2013-09-05</qwe>
</root>

演示

import java.io.File;
import javax.xml.bind.*;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum18617998/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

输出

请注意,在编组的XML中,空的Date字段被编组为缺失元素(参见:http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html)。

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <qwe>2013-09-05</qwe>
</root>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接