Jaxb 是否可以解组到接口?
Is it possible for Jaxb to unmarshall to an interface?
我当前的代码编组非常完美,我在结果 XML 中得到了我想要的元素。即 <food>Beef</food>
但是,当我必须将其解组回 java 对象时,问题就来了。一切 returns 都很好,除了食物变量。我最初没有 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}
}
我只是想知道这是否正确,没有真正好的方法来解组接口类型。这是真的?我看到很多其他问题都是关于编组接口的,而解组问题并没有真正得到令我满意的答案。任何答案表示赞赏,我知道这不是一个最小的可重现示例,但我更想寻找口头答案而不是代码修复或任何东西。不过,如果代码中有任何明显错误,请告诉我!
对于标准情况,JAXB 只能使用(抽象)类 而不是接口。
我能想到的选项
- 您可以使用
@XmlAdapter
的接口。参见示例:[1]
- 对 JAXB 绑定使用
Object
并通过转换公开接口。 (也许将验证逻辑添加到 `afterUnmarshal(Unmarshaller u, Object parent) 中。[2]
- 给
@XmlAnyElement
绑定一个私有字段,在afterUnmarshal(Unmarshaller, Object)
做一些进一步的处理,给目标添加@XmlTransient
。参见示例:[3]
如果有一些创意,可能会有一些其他的选择。但我认为一切都归结为基本:尝试进入 "raw" 解析选项并手动填写接口参考。
[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
}
}
我当前的代码编组非常完美,我在结果 XML 中得到了我想要的元素。即 <food>Beef</food>
但是,当我必须将其解组回 java 对象时,问题就来了。一切 returns 都很好,除了食物变量。我最初没有 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}
}
我只是想知道这是否正确,没有真正好的方法来解组接口类型。这是真的?我看到很多其他问题都是关于编组接口的,而解组问题并没有真正得到令我满意的答案。任何答案表示赞赏,我知道这不是一个最小的可重现示例,但我更想寻找口头答案而不是代码修复或任何东西。不过,如果代码中有任何明显错误,请告诉我!
对于标准情况,JAXB 只能使用(抽象)类 而不是接口。
我能想到的选项
- 您可以使用
@XmlAdapter
的接口。参见示例:[1] - 对 JAXB 绑定使用
Object
并通过转换公开接口。 (也许将验证逻辑添加到 `afterUnmarshal(Unmarshaller u, Object parent) 中。[2] - 给
@XmlAnyElement
绑定一个私有字段,在afterUnmarshal(Unmarshaller, Object)
做一些进一步的处理,给目标添加@XmlTransient
。参见示例:[3]
如果有一些创意,可能会有一些其他的选择。但我认为一切都归结为基本:尝试进入 "raw" 解析选项并手动填写接口参考。
[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
}
}