我们的模型类使用@XmlJavaTypeAdapter进行注释(在类级别)。对于根元素和包含/嵌套(根据我们在自定义XmlAdapter中实现的内容),解组工作正常。

到目前为止,我们对XML和JSON序列化/反序列化都很满意。但是,出现了新的需求,我不知道如何实现它?

在某些情况下,我希望能够“恢复”为默认的JAXB行为以进行遏制:我希望忽略/覆盖类级别的@XmlJavaTypeAdapter批注。

我花了几个小时阅读Blaise Doughan的博客(http://blog.bdoughan.com/)并搜索StackOverflow和Google,但找不到一个优雅/实用的解决方案。

这是一个快速设置,以说明我们当前拥有的内容(请注意,为简单起见,未列出所有JPA / Hibernate /其他注释,但它们确实存在于我们的模型类(POJO)中):

班主任

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlJavaTypeAdapter(XmlMasterAdapter.class)
public class Master {
    @XmlElement
    private Long masterPrimaryKey;

    @XmlElement
    private String name;
}

class 详情
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlJavaTypeAdapter(XmlDetailAdapter.class)
public class Detail {
    @XmlElement
    private Long detailPrimaryKey;

    @XmlElement
    private Master master; // reference/foreign key. No need for @XmlJavaTypeAdapter since it's defined at the class-level in Master.

    @XmlElement
    private String value;
}

当将Master用作根元素时,XML如下所示:
<master>
    <masterPrimaryKey>1234</masterPrimaryKey>
    <name>master name</name>
</master>

当将Master用作包含/嵌套的元素时,XML如下所示:(由于我们的自定义XmlAdapter,元素被其主键“汇总”了)
<detail>
    <detailPrimaryKey>5678</detailPrimaryKey>
    <master>1234</master>
    <value>detail value</value>
</detail>

到目前为止,一切正常,我们对此感到满意。

现在,我们的新需求:

我希望遏制在特定情况下以不同的方式工作。
我希望在特定上下文中“暂时”忽略Master上的类级别@XmlJavaTypeAdapter。我希望可以使用默认的JAXB解组器(好像所包含的类上从未存在过类级别的@XmlJavaTypeAdapter)。

考虑一种数据导入的情况,在这种情况下,我们在一个有效负载中接收到母版和所有详细信息。好像它们都是包裹在大DTO /运输容器中的独立根元素一样。

这是表示我们想要的XML:
<masterDetailImport>
    <master>
        <!-- Primary keys omitted because of the import mode -->
        <name>master name</name>
    </master>

    <details>
        <detail>
            <value>detail 1 value</value>
        </detail>

        <detail>
            <value>detail 2 value</value>
        </detail>

        <detail>
            <value>detail 3 value</value>
        </detail>
    </details>
</masterDetailImport>

类MasterDetailImport
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class MasterDetailImport implements Serializable
{
    @XmlElement
    @PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
    private Master master;

    @XmlElementWrapper(name="details")
    @XmlElement
    @PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
    private List<Detail> detail = new ArrayList<Detail>();
}

我正在寻找的是[尚不存在的]神奇的@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WE__A_ROOT_ELEMENT批注,该批注使我可以指示JAXB像从未在嵌套类的类级别定义@XmlJavaTypeAdapter一样。

到目前为止,我们设想的[并且不喜欢]的解决方案是:
  • 仅当我们必须支持导入时,才创建用于反序列化的“镜像” DTO对象。这种方法有很多缺点(重复的代码仅用于反序列化,将DTO内容复制到模型类的适配器,更多的写入/维护的单元测试等)。
  • 在我们希望能够导入/嵌套的所有实体上摆脱类级别的@XmlJavaTypeAdapter,并在所有使用嵌套/包含的属性上显式使用@XmlJavaTypeAdapter。我测试了这种方法,并且知道它会起作用。但是,我认为它易于出错,不如在类级别定义它那么优雅,并且能够具有异常/特殊情况/重写处理,使JAXB暂时表现为好像从未知道在类上定义过@XmlJavaTypeAdapter 。

  • 我在这里没什么主意...我试图寻找JAXB的默认XML适配器,但没有成功:javax.xml.bind.annotation.adapters.XmlAdapter 是抽象的,并且继承自Java.lang.Object 。

    现在,一个简单的问题:
    如何实现@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT?

    提前致谢 !

    最佳答案

    JAXB中的XmlAdapter将始终被应用,但是您可以在XmlAdapter本身中放入逻辑来处理用例。默认情况下,每次使用XmlAdapter时都会创建一个新实例,如果XmlAdapter是有状态的,则可以在MarshallerUnmarshaller上设置一个实例,以代替使用它。您可以利用它来帮助确定是否应该应用它。

    以下是我对一个相关问题的答案的链接,在该问题中,有状态XmlAdapter用于在首次引用对象时内联该对象,然后在每次引用该对象时将其封送为链接。

  • Can JAXB marshal by containment at first then marshal by @XmlIDREF for subsequent references?
  • 07-25 23:57