当前位置: 首页 > 知识库问答 >
问题:

JAXB:如何取消编组不同类型的对象列表,但具有共同的父对象?

隗高旻
2023-03-14

在我们的应用程序中有一个相当常见的模式。我们在 Xml 中配置一组(或列表)对象,这些对象都实现了一个公共接口。在启动时,应用程序读取 Xml 并使用 JAXB 创建/配置对象列表。我从来没有想出(在多次阅读各种帖子之后)仅使用JAXB来做到这一点的“正确方法”。

例如,我们有一个接口Fee,以及多个具体的实现类,它们具有一些共同的属性,以及一些不同的属性和非常不同的行为。我们用来配置应用程序使用的费用列表的Xml是:

<fees>
   <fee type="Commission" name="commission" rate="0.000125" />
   <fee type="FINRAPerShare" name="FINRA" rate="0.000119" />
   <fee type="SEC" name="SEC" rate="0.0000224" />
   <fee type="Route" name="ROUTES">
       <routes>
        <route>
            <name>NYSE</name>
            <rates>
                <billing code="2" rate="-.0014" normalized="A" />
                <billing code="1" rate=".0029" normalized="R" />
            </rates>
        </route>        
        </routes>
          ...
    </fee>
  </fees>

在上面的Xml中,每个

我总是不得不这样做:

private void addFees(TradeFeeCalculator calculator) throws Exception {
    NodeList feeElements = configDocument.getElementsByTagName("fee");
    for (int i = 0; i < feeElements.getLength(); i++) {
        Element feeElement = (Element) feeElements.item(i);
        TradeFee fee = createFee(feeElement);
        calculator.add(fee);
    }
}

private TradeFee createFee(Element feeElement) {
    try {
        String type = feeElement.getAttribute("type");
        LOG.info("createFee(): creating TradeFee for type=" + type);
        Class<?> clazz = getClassFromType(type);
        TradeFee fee = (TradeFee) JAXBConfigurator.createAndConfigure(clazz, feeElement);
        return fee;
    } catch (Exception e) {
        throw new RuntimeException("Trade Fees are misconfigured, xml which caused this=" + XmlUtils.toString(feeElement), e);
    }
}

在上面的代码中,JAXB 配置器只是一个围绕 JAXB 对象的简单包装器,用于取消编组:

public static Object createAndConfigure(Class<?> clazz, Node startNode) {
    try {
        JAXBContext context = JAXBContext.newInstance(clazz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        @SuppressWarnings("rawtypes")
        JAXBElement configElement = unmarshaller.unmarshal(startNode, clazz);
        return configElement.getValue();
    } catch (JAXBException e) {
        throw new RuntimeException(e);
    }
}

最后,在上面的代码中,我们得到一个列表,其中包含在Xml中配置的任何类型。

有没有一种方法可以让JAXB自动做到这一点,而不必像上面那样编写代码来迭代元素?


共有3个答案

曹沛
2023-03-14

我认为如果所有元素都被命名,这是不可能的

您是否能够根据类型重命名各种费用元素(例如

否则,您可以创建一个BaseFee类,其中包含每种可能的类型的所有字段

List<BaseFee> fees = ...;
for (BaseFee fee : fees) {
    if (isTradeFee(fee)) {
        TradeFee tradeFee = toTradeFee(fee);
        // do something with trade fee...
    }
}

有点难,但考虑到要求,它应该能完成这项工作。

方宜
2023-03-14
匿名用户

对于这个用例,您可以使用一个< code>XmlAdapter。impl bleow只处理< code>Commission类型,但可以很容易地扩展到支持所有类型。您需要确保< code>AdaptedFee包含来自< code>Fee接口的所有实现的组合属性。

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class FeeAdapter extends XmlAdapter<FeeAdapter.AdaptedFee, Fee>{

    public static class AdaptedFee {

        @XmlAttribute
        public String type;

        @XmlAttribute
        public String name;

        @XmlAttribute
        public String rate;

    }

    @Override
    public AdaptedFee marshal(Fee fee) throws Exception {
        AdaptedFee adaptedFee = new AdaptedFee();
        if(fee instanceof Commission) {
            Commission commission = (Commission) fee;
            adaptedFee.type = "Commission";
            adaptedFee.name = commission.name;
            adaptedFee.rate = commission.rate;
        }
        return adaptedFee;
    }

    @Override
    public Fee unmarshal(AdaptedFee adaptedFee) throws Exception {
        if("Commission".equals(adaptedFee.type)) {
            Commission commission = new Commission();
            commission.name = adaptedFee.name;
            commission.rate = adaptedFee.rate;
            return commission;
        }
        return null;
    }

}

Xml适配器是使用@XmlJavaTypeAdapter批注配置的:

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Fees {

    @XmlElement(name="fee")
    @XmlJavaTypeAdapter(FeeAdapter.class)
    private List<Fee> fees;

}

更多信息

    < Li > http://blog . bdoughan . com/2012/01/JAXB-and-inheritance-using-XML adapter . html

郁宾鸿
2023-03-14

注意:我是EclipseLink JAXB (MOXy)的负责人,也是JAXB (JSR-222)专家组的成员。

如果您使用MOXy作为JAXB提供者,则可以使用MOXy's@XmlPaths注释来扩展标准JAXB@XmlElementsannotation来执行以下操作:

费用

import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;

@XmlRootElement
public class Fees {

    @XmlElements({
        @XmlElement(type=Commission.class),
        @XmlElement(type=FINRAPerShare.class),
        @XmlElement(type=SEC.class),
        @XmlElement(type=Route.class)
    })
    @XmlPaths({
        @XmlPath("fee[@type='Commission']"),
        @XmlPath("fee[@type='FINRAPerShare']"),
        @XmlPath("fee[@type='SEC']"),
        @XmlPath("fee[@type='Route']")
    })
    private List<Fee> fees;

}

佣金

Fee接口的实现通常会被注释。

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Commission implements Fee {

    @XmlAttribute
    private String name;

    @XmlAttribute
    private String rate;

}

更多信息

  • http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
  • http://blog.bdoughan.com/2010/10/jaxb-and-xsd-choice-xmlelements.html
  • http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
 类似资料:
  • 问题内容: 在我们的应用程序中有一个相当普遍的模式。我们在Xml中配置一组配置对象(或列表)的对象,它们全部实现一个公共接口。在启动时,应用程序读取Xml并使用JAXB创建/配置对象列表。我从来没有想过(多次阅读各种文章之后)仅使用JAXB的“正确方法”。 例如,我们有一个interface ,以及多个具体的实现类,它们具有一些共同的属性,一些不同的属性以及非常不同的行为。我们用来配置应用程序使用

  • 问题内容: 我对此进行了扩展,应该可以帮助我在上下文之间传输对象: 现在它返回的对象,我应该将其强制转换为我想要的类,如下所示: 有没有办法避免这种无用的转换,如果我从类的对象调用使其返回类型? 问题答案: 更新: 有关更好的解决方案 与如何在NSManagedObjectSwift扩展中如何创建托管对象子类的实例中类似,这可以使用通用的辅助方法来完成: 请注意,我已将返回类型更改为。 并 没有

  • 尝试使用mapstruct将两个列表映射到另一个列表时遇到问题。结构如下: 我需要将来自类C和类B的列表映射到类A中的列表(从对象B和对象C到对象A的映射已经完成)。我尝试了以下方法: 这给我带来了编译错误。有人能帮我映射这些列表吗?我听说过一个名为异常的注释参数,但无法解决我的问题。 谢谢

  • 我想实现一个功能,其中请求映射到正确的对象。是否有一种方法(除了自定义反序列化器)可以将请求按类型/基数映射到适当的对象?任何见解都将不胜感激!

  • 问题内容: 如何使用Gson解析此JSON?我有一个具有多个对象类型的数组,但我不知道需要创建哪种对象来保存此结构。我无法更改json消息(我无法控制服务器)。 唯一起作用的类是 JSON消息 (请注意具有多个对象类型的数组。) 问题答案: 《 Gson用户指南》明确涵盖了以下内容: https://sites.google.com/site/gson/gson-user-guide#TOC-Se