微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

调用 JAXB getter 会导致输出类型为 xs:list 的空属性值

如何解决调用 JAXB getter 会导致输出类型为 xs:list 的空属性值

我正在尝试处理调用 jaxb 生成的 getter(对于 xs:list 类型的属性值)导致创建空 ArrayList,然后将其编组到输出 XML 时为空字符串。空字符串属性输出破坏了其他生产代码,因此我希望不要输出这些(如果它们不存在于输入 XML 中)。

我尝试使用自定义 XmlAdapter marshall 方法,但是当我配置它时,jaxb 生成的 getter 实现已更改,它不再创建一个空的 ArrayList,它会使用 NullPointerExceptions 破坏现有代码

这里是更详细的背景信息。 我正在使用 jaxb 从 XML 模式生成 Java 类。 这是通过 ANT 目标完成的:

<java classname="com.sun.tools.internal.xjc.XJCFacade" failonerror="true">
    <arg value="-d"/>
    <arg value="${generated.dir}"/>
    <arg value="-p" />
    <arg value="com.myorg.model"/>
    <arg value="-b" />
    <arg value="${basedir}/bindings.xjb"/>
    <arg value="${schema.dir}/Test.xsd" />
</java>

架构 Test.xsd 文件

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.opentravel.org/OTA/2003/05" targetNamespace="http://www.opentravel.org/OTA/2003/05" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:simpleType name="Ref_Type">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{1,8}"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:simpleType name="listofRefs">
        <xs:list itemType="Ref_Type"/>
    </xs:simpleType>
    <xs:element name="RootElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="ChildElement" minOccurs="0" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:attribute name="MyRefs" type="listofRefs"/>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

jaxb 生成的类 RootElement.java:

//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation,v2.2.4-2 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2021.04.28 at 05:17:16 PM BST 
//


package com.myorg.model;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccesstype;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;


@XmlAccessorType(XmlAccesstype.FIELD)
@XmlType(name = "",propOrder = {
    "childElement"
})
@XmlRootElement(name = "RootElement")
public class RootElement {

    @XmlElement(name = "ChildElement")
    protected List<RootElement.ChildElement> childElement;

     public List<RootElement.ChildElement> getChildElement() {
        if (childElement == null) {
            childElement = new ArrayList<RootElement.ChildElement>();
        }
        return this.childElement;
    }

   @XmlAccessorType(XmlAccesstype.FIELD)
    @XmlType(name = "")
    public static class ChildElement {

        @XmlAttribute(name = "MyRefs")
        protected List<String> myRefs;

        public List<String> getMyRefs() {
            if (myRefs == null) {
                myRefs = new ArrayList<String>();
            }
            return this.myRefs;
        }

    }

}

注意 getMyRefs() 将创建一个新的空 ArrayList 如果 myRefs == null

我遇到的问题是,在将一些输入 XML 解组到 RootElement 对象之后,然后调用 getMyRefs() 将导致一个空的 MyRefs=" " 将其编组为字符串时要输出属性

例如,如果我输入以下内容

<RootElement>
    <ChildElement/>
    <ChildElement MyRefs="1 2 3 4"/>
    <ChildElement/>
</RootElement>

如果我将其解组到 RootElement 对象并调用 getter:

List<String> myRefs = rootElement.getChildElement().get(0).getMyRefs();

然后我将 rootElement 编组回字符串,它将是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootElement xmlns="http://www.opentravel.org/OTA/2003/05">
    <ChildElement MyRefs=""/>
    <ChildElement MyRefs="1 2 3 4"/>
    <ChildElement/>
</RootElement>

你可以看到第一个 ChildElement 现在有一个空的 MyRefs 属性,第三个 ChildElement 没有,因为我没有调用rootElement.getChildElement().get(2).getMyRefs()

我的要求是第一个 ChildElement 不会输出任何 MyRefs 属性

我尝试的解决方案是修改我的 bindings.xjb 文件

<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
          xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
          xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:ota="http://www.opentravel.org/OTA/2003/05"
          version="2.1">
          
    <!-- This file handles exceptions in the JAXB source code generation-->
          
    <!-- Global type bindings -->
    <globalBindings typesafeEnumMaxMembers="380">
        <javaType name="java.util.List&lt;String&gt;" xmlType="ota:listofRefs" parseMethod="com.myorg.model.utils.AttributeListConverter.unmarshal" printMethod="com.myorg.model.utils.AttributeListConverter.marshal"/>
    </globalBindings>
</bindings>

我的 AttributeListConverter 类:

package com.myorg.model.utils;

import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;

public class AttributeListConverter {
    private static final Splitter SPACE_SPLITTER = Splitter.on(' ').trimResults().omitEmptyStrings();

    public static List<String> unmarshal(String spaceSeparatedList) {
        return StringUtils.isEmpty(spaceSeparatedList) ? new ArrayList<String>() : SPACE_SPLITTER.splitToList(spaceSeparatedList);
    }
    
    public static String marshal(List<String> attributeList) {
        return (attributeList == null || attributeList.isEmpty()) ? null : Joiner.on(" ").join(attributeList);
    }
}

ma​​rshal() 方法中,当 List 为 null 或为空时返回 nullMyRefs 属性不会输出。 >

但是,自动生成的 RootElement.java 类现在已更改:

@XmlAccessorType(XmlAccesstype.FIELD)
@XmlType(name = "")
public static class ChildElement {

    @XmlAttribute(name = "MyRefs")
    @XmlJavaTypeAdapter(Adapter28 .class)
    protected List<String> myRefs;

    /**
     * Gets the value of the myRefs property.
     * 
     * @return
     *     possible object is
     *     {@link java.lang.String }
     *     
     */
    public List<String> getMyRefs() {
        return myRefs;
    }

    /**
     * Sets the value of the myRefs property.
     * 
     * @param value
     *     allowed object is
     *     {@link java.lang.String }
     *     
     */
    public void setMyRefs(List<String> value) {
        this.myRefs = value;
    }

}

getMyRefs() 方法不再检查 null,并返回一个空的 ArrayList,它只是直接返回 myRefs 值。

这会破坏其他始终假定 getMyRefs() 永远不会为 null 的代码(它们现在抛出 NullPointerExceptions)的副作用。

使用自定义 marsall() 方法确实解决输出空 MyRefs 属性的问题,但是它破坏了依赖于 getMyRefs() 不返回 null 的其他功能

有什么方法可以获得我需要的行为吗?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。