JAXB Marshal空字符串全局为空

J B*_*lay 5 java jaxb

我的问题非常类似于当字符串为空但不为空时如何防止在JAXB中编组空标记

不同之处在于我无法将注释添加到package-info.java,因为我们所有的JAXB类型都是从每次构建的模式生成的.如果可能的话,我也更愿意不更改JAXB提供程序.

我想要实现的是设置一个空String不会创建该元素,但我需要为许多模式中的所有生成的JAXB类型设置它.有没有办法将它应用于所有生成的JAXB类中的所有String字段?

更新 我已设法通过进行以下更改来为模式中的所有字符串生成XML适配器:

在项目POM中,我将其添加到maven-jaxb2-plugin:

<bindingDirectory>src/main/resources</bindingDirectory>
<bindingIncludes>
    <include>bindings.xjb</include>
</bindingIncludes>
Run Code Online (Sandbox Code Playgroud)

这是我的bindings.xjb文件:

<jxb:bindings xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb" version="2.1">
    <jxb:globalBindings>
                    <jxb:javaType name="java.lang.String" xmlType="xs:token"
                        parseMethod="com.project.Formatter.parseString"
                        printMethod="com.project.Formatter.printString"/>
    </jxb:globalBindings>
</jxb:bindings>
Run Code Online (Sandbox Code Playgroud)

和格式化方法:

public static String printString(final String value)
{
    if (StringUtils.isBlank(value))
    {
        return null;
    }

    return value;
}
Run Code Online (Sandbox Code Playgroud)

问题是这会导致JAXB内部出现空指针异常.这是堆栈跟踪:

Caused by: java.lang.NullPointerException
    at com.sun.xml.bind.v2.runtime.output.SAXOutput.text(SAXOutput.java:158)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.leafElement(XMLSerializer.java:321)
    at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$1.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:210)
    at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$1.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:209)
    at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.writeLeafElement(TransducedAccessor.java:250)
    at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.serializeBody(SingleElementLeafProperty.java:98)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:150)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
    at com.sun.xml.bind.v2.runtime.property.ArrayElementNodeProperty.serializeItem(ArrayElementNodeProperty.java:65)
    at com.sun.xml.bind.v2.runtime.property.ArrayElementProperty.serializeListBody(ArrayElementProperty.java:168)
    at com.sun.xml.bind.v2.runtime.property.ArrayERProperty.serializeBody(ArrayERProperty.java:152)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:150)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:150)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:322)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:681)
    at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:150)
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:156)
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:185)
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:305)
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:312)
    at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:71)
    at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:490)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)
    at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:257)
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:103)
Run Code Online (Sandbox Code Playgroud)

导致此问题的原因归结为此方法:

com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.CompositeTransducedAccessorImpl.hasValue(BeanT)
Run Code Online (Sandbox Code Playgroud)

如果在运行任何适配器之前该值不为null ,则上述方法将呈现该元素.

有没有办法覆盖JAXB中使用的Accessor,以便在确定是否呈现元素之前运行适配器?还有另一种方法可以实现我想要的吗?

bdo*_*han 13

注意: 我是EclipseLink JAXB(MOXy)的负责人,也是JAXB(JSR-222)专家组的成员.

你所做的是对的,你看到的错误是由于我认为是JAXB参考实现中的一个错误.JAXB RI应该能够处理从一个返回的空值XmlAdapter.这个用例适用于EclipseLink JAXB(MOXy),我将在下面举例说明.

StringAdapter

下面的内容大致与您从XML模式生成Java模型后所获得的内容大致相同(请参阅http://blog.bdoughan.com/2011/08/xml-schema-to-java-generating. HTML).

package forum11894193;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class StringAdapter extends XmlAdapter<String, String> {

    @Override
    public String marshal(String string) throws Exception {
        if("".equals(string)) {
            return null;
        }
        return string;
    }

    @Override
    public String unmarshal(String string) throws Exception {
        return string;
    }

}
Run Code Online (Sandbox Code Playgroud)

包信息

由于您正在注册全局适配器,因此将从package-info类如下的类引用它(请参阅:http: //blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(value=StringAdapter.class, type=String.class)
})
package forum11894193;

import javax.xml.bind.annotation.adapters.*;
Run Code Online (Sandbox Code Playgroud)

下面是一个包含几个String字段的示例域类.由于XmlAdapter在包级别注册,它将应用于该包中的所有映射的字符串字段/属性.

package forum11894193;

import javax.xml.bind.annotation.*;

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

    String a;
    String b;
    String c;

}
Run Code Online (Sandbox Code Playgroud)

演示

在下面的演示代码中,我们将创建一个Root设置几个字段的实例,""然后将其编组为XML.

package forum11894193;

import javax.xml.bind.*;

public class Demo {

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

        Root root = new Root();
        root.a = "";
        root.b = "b";
        root.c = "";

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

}
Run Code Online (Sandbox Code Playgroud)

使用JAXB RI输出

在此示例中使用JAXB RI会生成NPE.堆栈跟踪是不同的,但我们最有可能使用不同的编组方法.我也在使用JDK中包含的JAXB RI版本,该版本被重新打包com.sun.xml.internal.bind.v2.

Exception in thread "main" java.lang.NullPointerException
    at com.sun.xml.internal.bind.v2.runtime.output.Encoded.setEscape(Encoded.java:96)
    at com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput.doText(UTF8XmlOutput.java:294)
    at com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput.text(UTF8XmlOutput.java:283)
    at com.sun.xml.internal.bind.v2.runtime.output.IndentingUTF8XmlOutput.text(IndentingUTF8XmlOutput.java:141)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.leafElement(XMLSerializer.java:293)
    at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$1.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:179)
    at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$1.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:166)
    at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.writeLeafElement(TransducedAccessor.java:239)
    at com.sun.xml.internal.bind.v2.runtime.property.SingleElementLeafProperty.serializeBody(SingleElementLeafProperty.java:87)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:561)
    at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:290)
    at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:462)
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:314)
    at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243)
    at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75)
    at forum11894193.Demo.main(Demo.java:17)
Run Code Online (Sandbox Code Playgroud)

使用EclipseLink JAXB(MOXy)输出

当MOXy用作JAXB提供程序时,您将获得所需的输出.有关将MOXy指定为JAXB提供程序的信息,请参阅:http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html .

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <b>b</b>
</root>
Run Code Online (Sandbox Code Playgroud)

  • 这解决了这个问题,谢谢你的帮助.我删除了问题的这一部分以简化视图.我可以在今天晚些时候奖励赏金.我还比较了生成的XML,它们在功能上是相同的,如你所说. (2认同)