19

我正在将一个项目从 JAXB 1.0 迁移到 JAXB 2.1,并且遇到了数据类型映射问题。

我正在使用 Ant xjc绑定编译器,并且我已经成功配置了全局绑定,以便(例如)xs:date映射到java.util.Calendar

但是,我得到了返回的生成方法Boolean,而我想要boolean

这是复杂类型:

<xs:element name="usage-auth-rate-charge">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="service-id" type="xs:string"/>
            <xs:element name="pricepoint_custom_fields_required" type="xs:boolean" minOccurs="0"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>

生成的类如下所示:

public class UsageAuthRateCharge {
........
public Boolean isPricepointCustomFieldsRequired() {
    return pricepointCustomFieldsRequired;
}

问题是虽然装箱可以工作,但如果提供的 XML 不包含 的值pricepoint_custom_fields_required,则该类的布尔字段为空,而不是假。所以我在做这样的事情时得到NullPointerExceptions :

methodWhichTakesPrimitiveBooleanArg(myUsageAuthRateChargeInstance.isPricepointCustomFieldsRequired());

因为它试图拆箱传入的布尔值 - 除了它是空的。


我无法更改架构,也无法调整所有客户端代码来进行空检查。

我在binding.xml中设置了optionalProperty属性,如下所示:

<globalBindings optionalProperty="primitive">

在规范中,它说:“如果属性的值是“原始的”,它会像在 JAXB 1.0 中那样绑定”

然而,这显然没有发生。

我怎么解决这个问题?

更新:

这现在在 jaxb 2.2.9 中得到修复: https ://java.net/jira/browse/JAXB/fixforversion/16850

4

4 回答 4

29

问题

你得到的原因是你在元素定义中Boolean。将为不存在的元素存储一个值。booleanminOccurs="0"null

<xs:element name="pricepoint_custom_fields_required" type="xs:boolean" minOccurs="0"/>

解决方案应该是什么

解决方案应该是一个外部绑定文件,该文件指示应将原始类型用于可选值(请参阅 JAXB 2.2 规范的第 7.5.1 节)。

<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings
    xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">
    <jaxb:globalBindings optionalProperty="primitive"/>
</jaxb:bindings>

外部绑定文件是使用-b带有 XJC 的选项指定的。

xjc -b binding.xml my-schema.xsd

为什么该解决方案不起作用

已打开以下错误以跟踪问题optionalProperty="primitive"

解决方法

如果您不喜欢在 JAXB 中生成类的方式,那么您可以自己创建一个类并让 JAXB 将其作为模式的一部分拉入到类生成中。

绑定.xml

<jxb:bindings 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    version="2.1">

    <jxb:bindings schemaLocation="my-schema.xsd">
        <jxb:bindings node="//xs:element[@name='usage-auth-rate-charge']/complexType">
            <jxb:class ref="com.example.foo.UsageAuthRateCharge"/>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

UsageAuthRateCharge

使用您想要的方法创建此类。

XJC 呼叫

使用-bXJC 调用上的标志来指定binding.xml文件

xjc -b binding.xml my-schema.xsd

更新

我真正希望的是其中一位读者可能是 jaxb 开发团队成员,他可以轻松地在 jaxb 中进行更改。用户 archenroot 在他对 jaxb 问题 927(926 的副本)的评论中提供了一个可能的解决方案,这让我认为对于合适的人来说,修复 jaxb 将是一项简单的工作

我是EclipseLink JAXB (MOXy)负责人。我已经联系了执行 JAXB 参考实现的同事(他们也为 Oracle 工作)。以下是我从他们那里得到的回复:

我已经在后备箱中进行了测试 - 问题存在。我将检查代码修复它的难易程度。

希望您能够真正解决此问题。

于 2013-12-13T15:21:04.570 回答
3

试试这个...

<xs:element name="usage-auth-rate-charge">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="service-id" type="xs:string"/>
        </xs:sequence>
        <xs:attribute name="chosen" type="xs:boolean" use="required"/>
    </xs:complexType>
</xs:element>

当我正在寻找如何做与您正在做的完全相反的事情时,我发现了您的问题。我有一个布尔属性,它只会生成将该属性作为原始布尔值的代码。为了使 jaxb 将此属性生成为布尔对象而不是布尔原语,我刚刚删除了 xsd 中属性定义的 use="required" 部分。

于 2012-10-25T22:02:53.077 回答
2

只是为了让它完整

type="xs:boolean" minOccurs="0" maxOccurs="1"                   == Boolean value (object)

type="xs:boolean" minOccurs="0" maxOccurs="1" nillable="true"   == JAXBElement<Boolean> value (object)

type="xs:boolean" minOccurs="1" maxOccurs="1"                   == boolean value (primitive)

type="xs:boolean" minOccurs="1" maxOccurs="1" nillable="true"   == Boolean value (object)
于 2018-09-24T08:14:09.593 回答
0

我厌倦了等待开发团队的修复,所以我卷起袖子自己动手。

我包括下面的代码来帮助那些这也是一个问题的人。

免责声明:我的代码可能不是解决问题的最佳方法,但它对我有用。

生成的代码现在如下所示:

public boolean isPricepointCustomFieldsRequired() {
    if (pricepointCustomFieldsRequired == null) {
        return false;
    } else {
        return pricepointCustomFieldsRequired;
    }
}

修改如下:

com.sun.tools.xjc.reader.xmlschema.bindinfo.BIProperty:createElementProperty, line ~358

线后

类型.addTo(prop);

插入以下代码:

if (prop.isOptionalPrimitive() && getOptionalPropertyMode() == OptionalPropertyMode.PRIMITIVE &&
    !prop.getTypes().isEmpty() && "boolean".equals(prop.getTypes().get(0).getTypeName().getLocalPart()) )   {
        prop.defaultValue= CDefaultValue.create(CBuiltinLeafInfo.BOOLEAN, new XmlString("false"));
    }
于 2014-01-10T12:36:39.520 回答