JAXB Marshal空字符串全局为空

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

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

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

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

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

src/main/resources  bindings.xjb  

这是我的bindings.xjb文件:

      

和格式化方法:

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

问题是这会导致JAXB内部出现空指针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中使用的Accessor,以便在确定是否呈现元素之前运行适配器? 还有另一种方法可以实现我想要的吗?

注意:我是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 { @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; } 

演示

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

   b  

空字符串仍然是一个值。 这就是元素创建的原因。 如上所述,如果字符串为空,在setter方法中将变量设置为null?

另外,检查这个线程JAXB:如何使JAXB NOT将空字符串解组为0

使用以下代码进入Marshall课程

 public static String toXml(Object o, Class clazz, boolean isFormatted, boolean isEmptyNodes) { try { Map properties = new HashMap(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); } }