使用JAXB XMLAnyElement类型的样式来返回动态元素名称

时间:2014-01-27 13:31:26

标签: java jaxb resteasy

我在这些论坛以及其他博客文章中已经阅读了很多答案,但我似乎无法将各个部分连接在一起。

因此,我们从包含Map属性的基本POJO开始。已经很好地确定了如何包装它,但这会返回一些值。我要做的是接受name(a.k.a。标签)并使其成为有效的XML'属性'。所以我们会得到一些价值。

我发现了一个例子(如果我能再次找到它会链接)如下:

@XmlAnyElement
public List<JAXBElement<String>> getXmlProperties() {
   List<JAXBElement<String>> elements = new ArrayList<JAXBElement<String>>();
   for (Map.Entry<String, String> property: properties.entrySet()) 
      elements.add(new JAXBElement<String>(new QName(property.getKey()), 
      String.class, property.getValue()));
      return elements;
}

这很好用,但我在Bean / Pojo类中有这个,它与GWT前端共享,因此不能包含对JAXBElement和QName的引用(需要源代码)。

那么,是否有办法使用XmlAdapter和JAXBElement / QName / XmlAnyElement梦之队获得类似的结果?顺便说一句,如果这个因素存在,我正在使用RESTEasy。

以下是@ XmlAnyElement + JAXBElement的论坛帖子:Dynamic tag names with JAXB

2 个答案:

答案 0 :(得分:4)

我距离答案还不远 - 经过一些实验后我发现了正确的组合。

为不可映射的返回类型创建包装类。包装器应包含/返回带有List<JAXBElement<String>的{​​{1}} Annotate包装器返回类型。

@XmlAnyElement

创建一个封送到MapWrapper的XmlAdapter

public class MapWrapper {
   @XmlAnyElement
    public List<JAXBElement<String>> properties = new ArrayList<JAXBElement<String>>();
}

使用XmlAdapter

注释您的不可映射类型
public class MapAdapter extends XmlAdapter<MapWrapper, Map<String,String>> {
    @Override
    public MapWrapper marshal(Map<String,String> m) throws Exception {
    MapWrapper wrapper = new MapWrapper();
    List<JAXBElement<String>> elements = new ArrayList<JAXBElement<String>>();
       for (Map.Entry<String, String> property: m.entrySet()) {
          elements.add(new JAXBElement<String>(
                    new QName(getCleanLabel(property.getKey())), 
          String.class, property.getValue()));
       }
       wrapper.elements=elements;
    return wrapper;
}

@Override
public Map<String,String> unmarshal(MapWrapper v) throws Exception {
            // TODO
    throw new OperationNotSupportedException();
}

// Return a lower-camel XML-safe attribute
private String getCleanLabel(String attributeLabel) {
    attributeLabel = attributeLabel.replaceAll("[()]", "")
            .replaceAll("[^\\w\\s]", "_").replaceAll(" ", "_")
            .toUpperCase();
    attributeLabel = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,
            attributeLabel);
    return attributeLabel;
}
}

地图如:

@XmlRootElement
public class SomeBean {

    @XmlJavaTypeAdapter(MapAdapter.class)
    public LinkedHashMap<String, String> getProperties() {
        return properties;
    }
}

应该出现:

My Property 1    My Value 1 
My Property 2    My Value 2

希望这有助于其他人!

答案 1 :(得分:-1)

取决于上面列出的解决方案,我进入了MapAdapter,它解决了编组和解组过程。

@XmlType
public class MapWrapper{
    private List<JAXBElement<String>> properties = new ArrayList<>();

    public MapWrapper(){

    }
    @XmlAnyElement
    public List<JAXBElement<String>> getProperties() {
        return properties;
    }
    public void setProperties(List<JAXBElement<String>> properties) {
        this.properties = properties;
    }
    @Override
    public String toString() {
        return "MapWrapper [properties=" + toMap() + "]";
    }


    public Map<String, String> toMap(){
        //Note: Due to type erasure, you cannot use properties.stream() directly when unmashalling is used.
        List<?> props = properties;
        return props.stream().collect(Collectors.toMap(MapWrapper::extractLocalName, MapWrapper::extractTextContent));
    }

    @SuppressWarnings("unchecked")
    private static String extractLocalName(Object obj){

        Map<Class<?>, Function<? super Object, String>> strFuncs = new HashMap<>();
        strFuncs.put(JAXBElement.class, (jaxb) -> ((JAXBElement<String>)jaxb).getName().getLocalPart());
        strFuncs.put(Element.class, ele -> ((Element) ele).getLocalName());
        return extractPart(obj, strFuncs).orElse("");
    }


    @SuppressWarnings("unchecked")
    private static String extractTextContent(Object obj){
        Map<Class<?>, Function<? super Object, String>> strFuncs = new HashMap<>();
        strFuncs.put(JAXBElement.class, (jaxb) -> ((JAXBElement<String>)jaxb).getValue());
        strFuncs.put(Element.class, ele -> ((Element) ele).getTextContent());
        return extractPart(obj, strFuncs).orElse("");
    }

    private static <ObjType, T> Optional<T> extractPart(ObjType obj, Map<Class<?>, Function<? super ObjType, T>> strFuncs){
        for(Class<?> clazz : strFuncs.keySet()){
            if(clazz.isInstance(obj)){
                return Optional.of(strFuncs.get(clazz).apply(obj));
            }
        }
        return Optional.empty();
    }
}

public class MapAdapter extends XmlAdapter<MapWrapper, Map<String, String>>{

    @Override
    public Map<String, String> unmarshal(MapWrapper v) throws Exception {
        return v.toMap();
    }

    @Override
    public MapWrapper marshal(Map<String, String> m) throws Exception {
        MapWrapper wrapper = new MapWrapper();

        for(Map.Entry<String, String> entry : m.entrySet()){
             wrapper.addEntry(new JAXBElement<String>(new QName(entry.getKey()), String.class, entry.getValue()));
        }

        return wrapper;
    }

}

我已发布完整帖子here (in another post),其中还提供了评论和示例..