具有特定名称的 JAXB 元素嵌套在另一个未知名称的元素中

问题描述 投票:0回答:2

我有一个

XmlRootElement(name = "Entity")
,其中包含一个已知的
XmlElement(name = "Header")

和另一个名称未知的 XmlElement,但已知它有一个内部
XmlElement(name="label")

以下是可能的 Xml:

<Entity>
   <Header>this is a header</Header>
   <a>
     <label>this is element A</label>
     <otherElements/>
   </a>
</Entity>

<Entity>
   <Header>this is a different header</Header>
   <b>
     <label>this is some other element of name b</label>
     <others/>
   </b>
</Entity>

这是我的 JAXB 带注释的类:

@XmlRootElement(name = "Entity")
@XmlAccessorType(XmlAccessType.NONE)
public class Entity {
    @XmlElement(name = "Header")
    private Header header;

    @XmlElements( {
       @XmlElement(name = "a", type=LabelledElement.A.class), 
       @XmlElement(name = "b", type=LabelledElement.B.class)
    } )
    private LabelledElement labelledElement;

    // constructors, getters, setters...
}

@XmlAccessorType(XmlAccessType.NONE)
public abstract class LabelledElement {
     @XmlElement
     private String label;
     @XmlAnyElement
     private List<Element> otherElements;

     public static class A extends LabelledElement {}
     public static class B extends LabelledElement {}
}

效果很好!但后来我发现它不仅仅是

<a>
<b>

可能是

<c>
<asd>
甚至
<anything>
...

所以列出

XmlElement(name = "xyz", type = LabelledElement.xyz.class)
显然不是正确的解决方案。

我只关心

Entity#getLabelledElement()#getLabel()
,不管
LabelledElement
的名字是什么。

如何做到这一点?这对于 JAXB 来说可能吗?

java xml jaxb
2个回答
1
投票

使用 EclipseLink JAXB 实现 (MOXy),这应该可以工作:

@XmlRootElement(name = "Entity")
@XmlSeeAlso({LabelledElement.class}) //Might not be necessary
@XmlAccessorType(XmlAccessType.NONE)
public class Entity {
    @XmlElement(name = "Header")
    private Header header;

    @XmlPath("child::*[position() = 2]")
    @XmlJavaTypeAdapter(MapAdapter.class)
    private Map<String,LabelledElement> labelledElementMap;


    public LabelledElement getLabelledElement(){
         return labelledElementMap.values().get(0);
    }
    // constructors, getters, setters...
}

MapAdapter
班级:

public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, LabelledElement>> {

    public static class AdaptedMap {

        @XmlVariableNode("key")
        List<LabbeledElement> entries = new ArrayList<LabbeledElement>();

    }

    public static class AdaptedEntry {

        @XmlTransient
        public String key;

        @XmlElement
        public LabelledElement value;

    }

    @Override
    public AdaptedMap marshal(Map<String, LabelledElement> map) throws Exception {
        AdaptedMap adaptedMap = new AdaptedMap();
        for(Entry<String, LabelledElement> entry : map.entrySet()) {
            AdaptedEntry adaptedEntry = new AdaptedEntry();
            adaptedEntry.key = entry.getKey();
            adaptedEntry.value = entry.getValue();
            adaptedMap.entries.add(adaptedEntry);
        }
        return adaptedMap;
    }

    @Override
    public Map<String, LabelledElement> unmarshal(AdaptedMap adaptedMap) throws Exception {
        List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
        Map<String, LabelledElement> map = new HashMap<String, LabelledElement>();
        for(AdaptedEntry adaptedEntry : adaptedEntries) {
            map.put(adaptedEntry.key, adaptedEntry.value);
        }
        return map;
    }

}

仅供参考,我的解决方案的灵感来自于此链接


0
投票

显然可以使用 EclipseLink JAXB (MOXy) 实现,它允许您注释接口变量,提供将 XML 绑定到 Java 时要使用的 Factory 类和方法,请参阅此 answer

(经过编辑以提供此方法的示例) 例如,您没有抽象类 LabelledElement,而是有一个 接口 LabelledElement

public interface LabelledElement {
    String getLabel();
}

然后让类 A 和 B 实现它,如下所示:

import javax.xml.bind.annotation.XmlElement;

public class A implements LabelledElement{

    private String label;

    @Override
    @XmlElement(name="label")
    public String getLabel() {
        return label;
    }
}

实体类注释如下:

@XmlRootElement(name = "Entity")
@XmlAccessorType(XmlAccessType.NONE)
public class Entity {
    @XmlElement(name = "Header")
    private Header header;

    @XmlRootElement
    @XmlType(
    factoryClass=Factory.class, 
    factoryMethod="createLabelledElement")
    private LabelledElement labelledElement;

    // constructors, getters, setters...
}

然后,正如我链接到的答案所暗示的那样,您需要一个 Factory 类,例如:

import java.lang.reflect.*;
import java.util.*;

public class Factory {

    public A createA() {
        return createInstance(A.class);
    }

    public B createB() {
        return createInstance(B.class);;
    }

    private <T> T createInstance(Class<T> anInterface) {
        return (T) Proxy.newProxyInstance(anInterface.getClassLoader(), new Class[] {anInterface}, new InterfaceInvocationHandler());
    }

    private static class InterfaceInvocationHandler implements InvocationHandler {

        private Map<String, Object> values = new HashMap<String, Object>();

        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String methodName = method.getName();
            if(methodName.startsWith("get")) {
                return values.get(methodName.substring(3));
            } else {
                values.put(methodName.substring(3), args[0]);
                return null;
            }
        }

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.