【问题标题】:Is it possible for Jaxb to unmarshall to an interface?Jaxb 是否可以解组到接口?
【发布时间】:2019-09-28 00:01:09
【问题描述】:

我当前的代码完美编组,并且我在生成的 XML 中获得了我想要的元素。即<food>Beef</food>

但是,当我必须将其解组回 java 对象时,问题就出现了。除了食物变量,一切都恢复正常。我最初没有顶部的XmlElement(required = true),并且食物元素总是会解组回null。然后,我添加了 required=true 部分,我遇到了界面问题。我做了一些挖掘,据我所知,jaxb 无法真正解组为接口,因为它不知道要编组的具体类型。

如果有帮助,当前错误:

Can not set FoodInterface field BigPayload.food to 
com.sun.org.apache.xerces.internal.dom.ElementNSImpl

我的Java类如下:

@XmlSeeAlso({MeatFoods.class, VeggieFoods.class})
@XmlType(name ="BigPayload", propOrder = //stuff goes here
@XmlRootElement(name = foodPayload)
public class BigPayload implements Payload{
    @XmlElements({@XmlElement(type = MeatFoods.class), 
                  @XmlElement(type = VeggieFoods.class),
                  @XmlElement(required = true)})
    protected FoodInterface food;
    protected Grade grade;
    //grade/food setters and getters
}
@XmlTransient //If this isn't here, I get the jaxB cannot handle interfaces and no default constructor error
public interface FoodInterface{ //stuff here}
@XmlType(name = "MeatFoods")
@XmlEnum
public enum MeatFoods implements FoodInterface{
    Chicken(1, Chicken)
    Beef(2, Beef)
    Pork(3, Pork)

    int value;
    String name;

    @Override
    public int getValue()

    @Override
    public String getName()

    public static FoodInterface getEnumFromValue(int value){//gets stuff}
    public static FoodInterface getEnumFromName(String name){//gets stuff}
}

我只是想知道这是否正确,并且没有真正好的方法来解组接口类型。这是真的?我看到很多其他问题是关于编组接口的,而解组问题并没有真正得到令我满意的答案。任何答案都值得赞赏,我知道这不是一个最小的可重复示例,但我更多的是寻找口头答案而不是代码修复或任何东西。不过,如果代码中有任何明显错误,请告诉我!

【问题讨论】:

  • 是的——这是可能的。我在接口类型的字段上使用@XmlElements,它成功地编组和解组。

标签: java interface jaxb marshalling unmarshalling


【解决方案1】:

对于标准情况,JAXB 只能使用(抽象)类而不是接口。

我能想到的选项

  • 您可以使用带有@XmlAdapter 的接口。参见示例:[1]
  • Object 用于 JAXB 绑定并通过强制转换公开接口。 (也许在 `afterUnmarshal(Unmarshaller u, Object parent) 中添加验证逻辑。[2]
  • 将私有字段绑定到@XmlAnyElement,并在afterUnmarshal(Unmarshaller, Object)中做一些进一步的处理,将@XmlTransient添加到目标中。参见示例:[3]

有了一些创意,可能还有其他一些选择。但我认为一切都归结为基本:尝试使用“原始”解析选项并手动填写接口参考。

[1]

public static interface Food {
    String name();
}
public enum Veggie implements Food {
    SALAD;
}
public static enum Meat implements Food {
    CHICKEN;
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlJavaTypeAdapter(FoodAdapter.class)
    @XmlAttribute
    private Food food;

    public Food getFood() {
        return food;
    }

    public void setFood(Food food) {
        this.food = food;
    }
}

public static class FoodAdapter extends XmlAdapter<String, Food> {

    @Override
    public Food unmarshal(String v) throws Exception {
        try {
            return Veggie.valueOf(v);
        } catch (IllegalArgumentException e) {

        }
        try {
            return Meat.valueOf(v);
        } catch (IllegalArgumentException e) {

        }
        throw new IllegalArgumentException("Unknown Food:" + v);
    }

    @Override
    public String marshal(Food v) throws Exception {
        return v.name();
    }

}

[2]

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlElement
    private Object food;

    public Food getFood() {
        return (Food) food;
    }

    public void setFood(Food food) {
        this.food = food;
    }

    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        if (food != null && !(food instanceof Food)) {
            throw new IllegalArgumentException("food is of wrong type: " + food.getClass().getName());
        }
    }
}

JAXBContext newInstance = JAXBContext.newInstance(UseInterface.class, Meat.class, Veggie.class);
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><useInterface><food xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"meat\">CHICKEN</food></useInterface>";

newInstance.createUnmarshaller().unmarshal(new StringReader(xml));

[3]

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public static class UseInterface {

    @XmlAnyElement
    private org.w3c.dom.Element foo;

    @XmlTransient
    private SomeInterface ifc


    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        NamedNodeMap attributes = foo.getAttributes();
        // do something with foo on DOM level to bind the subtree to an interface manually
    }
}

【讨论】:

    猜你喜欢
    • 2013-05-27
    • 1970-01-01
    • 2021-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多