Use JAXB XMLAnyElement type de style to return dynamic element names

j'ai lu beaucoup de réponses dans ces forums, ainsi que d'autres Billets de blog, mais je ne peux pas tout à fait sembler relier les morceaux ensemble.

ainsi, nous commençons par un POJO de base contenant une carte des propriétés. Il est bien établi comment envelopper ceci, mais cela renvoie une certaine valeur. Ce que je cherche à faire, c'est de prendre puis de nommer (A. K. A. label) et en faire un attribut XML valide. Donc, on devrait obtenir une certaine valeur.

j'ai trouvé un exemple (lien si je peux trouver it again) comme suit:

@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;
}

cela a parfaitement fonctionné, mais je l'avais dans ma classe Bean/Pojo, qui est partagée avec un front-end GWT, donc ne peut pas contenir de références à JAXBElement et QName (code source requis).

alors, y a-t-il un moyen d'obtenir un résultat similaire en utilisant quelque chose comme XmlAdapter, et L'équipe de rêve JAXBElement/QName/XmlAnyElement? Au fait,J'utilise RESTEasy si ça compte.

Voici le post du forum avec le @xmlanyelement+JAXBElement: les noms des étiquettes dynamiques avec JAXB

2
demandé sur Community 2014-01-27 17:31:26

2 réponses

Je n'étais pas si loin de la réponse - après un peu plus d'expérimentation, j'ai trouvé le bon combo.

créer une classe d'enrubannage pour le type de retour non cartographiable. L'emballage doit contenir / retourner List<JAXBElement<String> Annoter le type de retour de l'emballage avec @XmlAnyElement .

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

Créer un XmlAdapter que les commissaires à la MapWrapper

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;
}
}

annoter votre type indisponible avec le XmlAdapter

@XmlRootElement
public class SomeBean {

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

une carte comme:

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

devrait sortir comme:

<someBean>
   <properties>
     <myProperty1>My Value 1</myProperty1>
     <myProperty2>My Value 1</myProperty2>
   </properties>
</someBean>

Espérons que cela aide quelqu'un d'autre!

4
répondu shawnjohnson 2014-01-28 15:25:54

dépend de la solution énumérée ci-dessus, je suis venu dans ce MapAdapter qui traite à la fois le processus de formation et de non-formation.

@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;
    }

}

j'ai publier le post complet ici (dans un autre post) , où les commentaires ainsi que des exemples sont également fournis..

-1
répondu mec_test_1 2017-05-23 12:25:26