如何在某些情况下忽略/禁用/恢复/覆盖JAXB类级别的@XmlJavaTypeAdapter?

时间:2013-11-07 17:08:26

标签: java xml jaxb

我们的模型类使用@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;
}

课程详情

@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,&lt; master&gt;元素由其主键“汇总”)

<detail>
    <detailPrimaryKey>5678</detailPrimaryKey>
    <master>1234</master>
    <value>detail value</value>
</detail>

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

现在,我们的新需求:

我希望遏制在特定情况下以不同的方式工作。 我希望Master上的类级别@XmlJavaTypeAdapter在特定上下文中“暂时”被忽略/恢复/覆盖。我希望默认的JAXB unmarshaller能够启动(好像从来没有在包含的类上有类级别的@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>

Class 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_WERE_A_ROOT_ELEMENT注释,这将允许我指示JAXB执行,就好像从未在类级别为嵌套类定义了@XmlJavaTypeAdapter。

到目前为止,我们设想[并且不喜欢]的解决方案是:

  1. 仅在我们必须支持导入时才创建“镜像”DTO对象以进行反序列化。这种方法有很多缺点(重复代码仅用于反序列化,适配器用于将DTO内容复制到模型类中,更多单元测试用于编写/维护等)。

  2. 在我们希望能够导入/嵌套的所有实体上删除类级别@XmlJavaTypeAdapter,并在使用嵌套/包含的所有属性上显式使用@XmlJavaTypeAdapter。我测试了这种方法,并知道它会起作用。但是,我认为它容易出错,并不像在类级别定义它那样优雅,并且能够有一个异常/特殊情况/覆盖处理,告诉JAXB暂时表现得好像它从来不知道已经在类上定义了@XmlJavaTypeAdapter

  3. 我在这里没有想法......我试图寻找JAXB的默认XML适配器但是没有成功:javax.xml.bind.annotation.adapters.XmlAdapter&lt; ValueType,BoundType&gt;是抽象的,并且继承自Java.lang.Object。

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

    提前致谢!

1 个答案:

答案 0 :(得分:2)

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

下面是我给相关问题的答案的链接,其中有状态XmlAdapter用于在第一次引用时内联对象,然后在每次引用时将其编组为链接