gpt4 book ai didi

java - JAXB 将空字符串全局编码为空

转载 作者:搜寻专家 更新时间:2023-10-31 08:20:33 25 4
gpt4 key购买 nike

我的问题与How to prevent marshalling empty tags in JAXB when string is empty but not null 非常相似

不同之处在于我无法将注释添加到 package-info.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 深处出现 Null Pointer Exception。这是堆栈跟踪:

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 中使用的访问器,以便在确定是否呈现元素之前运行适配器?还有其他方法可以实现我想要的吗?

最佳答案

注意:我是 EclipseLink JAXB (MOXy) JAXB (JSR-222) 的领导和成员专家组。

您所做的是正确的,您看到的错误是由于我认为是 JAXB reference implementation 中的错误所致. 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 是在包级别注册的,它将应用于该包中所有映射的字符串字段/属性。

package forum11894193;

import javax.xml.bind.annotation.*;

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

String a;
String b;
String c;

}

演示

在下面的演示代码中,我们将创建一个 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);
}

}

使用 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)

使用 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>

关于java - JAXB 将空字符串全局编码为空,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11894193/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com