当前位置: 首页 > 知识库问答 >
问题:

JAXB将空字符串全局封送为Null

郎鸿雪
2023-03-14

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

区别在于我无法将注释添加到包信息。java作为我们所有的JAXB类型都是从每个构建的模式中生成的。如果可能的话,我也不希望更改JAXB提供程序。

我想要实现的是,设置空字符串不会创建元素,但我需要为许多模式中生成的所有JAXB类型设置此值。有没有办法将其应用于所有生成的JAXB类中的所有字符串字段?

更新我已通过以下更改成功使XML适配器为模式中的所有字符串生成:

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

<bindingDirectory>src/main/resources</bindingDirectory>
<bindingIncludes>
    <include>bindings.xjb</include>
</bindingIncludes>

这是我的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>

以及格式化方法:

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

    return value;
}

问题是,这会在JAXB内部深处导致空指针异常。这是stacktrace:

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)

这个问题的原因归结为这种方法:

com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.CompositeTransducedAccessorImpl.hasValue(BeanT)

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

有没有办法覆盖JAXB中使用的访问器,以便在确定是否呈现元素之前运行适配器?有没有其他方法可以实现我想要的?

共有3个答案

周和歌
2023-03-14

使用下面的代码进入你的Marshall类

public static String toXml(Object o, Class clazz, boolean isFormatted, boolean isEmptyNodes) {
        try {
            Map<String, Object> properties = new HashMap<String, Object>(1);
            if(isEmptyNodes) {
                SessionEventListener sessionEventListener = new NullPolicySessionEventListener();
                properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
            } else {
                SessionEventListener sessionEventListener = new DiscardEmptyTagSessionEventListener();
                properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
            }

            // Create a JaxBContext
            JAXBContext jc = JAXBContext.newInstance(new Class[] {clazz}, properties);
            StringWriter sw = new StringWriter();

            // Create the UnMarshaller Object using the JaxB Context
            Marshaller marshaller = jc.createMarshaller();

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isFormatted);
            // remove the xml version line from the output
            marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
            // Marshal the employee object to XML and print the output to console
            marshaller.marshal(o, sw);

            return sw.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
}
斜光耀
2023-03-14

空字符串仍然是值。这就是创建元素的原因。那么,如果字符串为空,那么在setter方法中如何将变量设置为null呢?

另外,检查这个线程JAXB:如何使JAXB不散集空字符串为0

童华池
2023-03-14

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

您所做的是正确的,您看到的错误是由于我认为JAXB参考实现中的一个错误。JAXB RI应该能够处理从XmlAdapter返回的空值。这个用例适用于EclipseLink JAXB(MOXy),我将在下面用一个示例演示。

字符串适配器

下面是一个实现,它大致实现了从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;
    }

}

软件包信息

由于您正在注册全局适配器,因此它将从如下所示的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.*;

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

package forum11894193;

import javax.xml.bind.annotation.*;

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

    String a;
    String b;
    String c;

}

演示

在下面的演示代码中,我们将创建一个根实例,并将几个字段设置为“XML”,然后将其封送到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);
    }

}

使用JAXB RI的输出

在本例中使用JAXB RI会产生NPE。堆栈跟踪是不同的,但我们很可能使用不同的封送处理方法。我还使用了JDK中包含的JAXB RI版本,该版本被重新打包到com。太阳xml。内部的绑定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)

使用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>
 类似资料:
  • 问题内容: 区别在于我无法将注释添加到package- info.java,因为我们的所有JAXB类型都是从每次构建的模式中生成的。如果可能的话,我也更愿意不更改JAXB提供程序。 我想要实现的是设置一个空的String不会创建该元素,但是我需要为从许多模式中生成的所有JAXB类型设置它。有没有办法将其应用于所有生成的JAXB类中的所有String字段? 更新 我已经通过进行以下更改来设法为模式中

  • 问题内容: 我有一个结构,其中包含要编码为JSON的字符串作为[] byte字段。但是,生成的JSON包含切片内容的非预期字符串表示形式。这是我所引用的示例: 这将产生以下输出: json.Marshal()方法对[] byte编码的字符串执行哪种转换。如何生成带有字符串{“ Content”:“ Hello”}的原始内容的JSON? 问题答案: 将A 编组为base64编码的字符串。从文档中:

  • 我正在开发一个客户端来使用 Web 服务,但由于某种原因,除非所有命名空间都正确且没有任何前缀,否则我的请求没有得到正确处理。 我所有的类都是由服务提供商使用提供的XSD和WSDL创建的。 NfeDadosMsg.class 包装信息.java TConsStatServ.class package br.inf.portalfiscal.nfe; 包装信息.java 输出xml: 由于某种原因,

  • 问题内容: 我有以下类,它是由Jackson映射的(简化版): 在某些情况下,服务器会返回,然后我想将name设置为空的Java String。 是否有任何Jackson注释,或者如果属性为,我应该只检查getter中的null并返回空字符串? 问题答案: 您可以在默认构造函数中或声明时进行设置: 要么

  • 我有以下由Jackson映射的类(简化版): 在某些情况下,服务器返回,然后我想将name设置为空Java字符串。 是否有任何Jackson注释,或者如果属性为,我应该只检查getter中的null并返回空字符串吗?

  • 我使用gson序列化java对象到json。 这个生成器处理好的空值。但与此同时,我希望它处理空字符串也为空。 这怎么能做到呢?