2010-07-19 7 views
19

Hay archivo XML fea que tiene que ser unmarshalled:Java/JAXB: XML Resolver referencia atribuye al objeto Java atributos específicos

<?xml version="1.0" ?> 
<configuration> 
    <section name="default_options"> 
     <value name="default_port">8081</value> 
     <value name="log_level">WARNING</value> 
    </section> 
    <section name="custom_options"> 
     <value name="memory">64M</value> 
     <value name="compatibility">yes</value> 
    </section> 
</configuration> 

resultantes objetos Java debe ser: respuesta

public class DefaultOptions { 
    private int defaultPort; 
    private String logLevel; 
    // etc... 
} 

public class CustomOptions { 
    private String memory; 
    private String compatibility; 
    // etc... 
} 

This de pregunta es muy cerca, pero no puedo entender la solución final.

+0

¿No puede cambiar los objetos java resultantes? En mi humilde opinión, sería mucho más fácil y más limpio si solo siguió la estructura xml (o la cambia para seguir la estructura que desea) –

+0

@Diego Dias, en realidad no. Tiene que haber POJO como estos. –

+0

¿Se puede agregar una superclase común a DefaultOptions & CustomOptions? –

Respuesta

16

¿Qué tal?

introducir un súper clase común que se llama Opciones:

import javax.xml.bind.annotation.XmlAttribute; 

public abstract class Options { 

    private String name; 

    @XmlAttribute 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

Entonces en su clase con la lista de opciones de configuración (en este ejemplo), especifican un @XmlJavaTypeAdapter en esa propiedad:

import java.util.ArrayList; 
import java.util.List; 

import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 

@XmlRootElement 
public class Configuration { 

    private List<Options> section = new ArrayList<Options>(); 

    @XmlJavaTypeAdapter(OptionsAdapter.class) 
    public List<Options> getSection() { 
     return section; 
    } 

    public void setSection(List<Options> section) { 
     this.section = section; 
    } 

} 

El XmlAdapter se verá algo como esto:

import javax.xml.bind.annotation.adapters.XmlAdapter; 

public class OptionsAdapter extends XmlAdapter<AdaptedOptions, Options> { 

    @Override 
    public Options unmarshal(AdaptedOptions v) throws Exception { 
     if("default_options".equals(v.name)) { 
      DefaultOptions options = new DefaultOptions(); 
      options.setName(v.getName()); 
      options.setDefaultPort(Integer.valueOf(v.map.get("default_port"))); 
      options.setLogLevel(v.map.get("log_level")); 
      return options; 
     } else { 
      CustomOptions options = new CustomOptions(); 
      options.setName(v.getName()); 
      options.setCompatibility(v.map.get("compatibility")); 
      options.setMemory(v.map.get("memory")); 
      return options; 
     } 
    } 

    @Override 
    public AdaptedOptions marshal(Options v) throws Exception { 
     AdaptedOptions adaptedOptions = new AdaptedOptions(); 
     adaptedOptions.setName(v.getName()); 
     if(DefaultOptions.class == v.getClass()) { 
      DefaultOptions options = (DefaultOptions) v; 
      adaptedOptions.map.put("default_port", String.valueOf(options.getDefaultPort())); 
      adaptedOptions.map.put("log_level", options.getLogLevel()); 
     } else { 
      CustomOptions options = (CustomOptions) v; 
      adaptedOptions.map.put("compatibility", options.getCompatibility()); 
      adaptedOptions.map.put("memory", options.getMemory()); 
     } 
     return adaptedOptions; 
    } 

} 

AdaptedOption s se ve así:

import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import java.util.Map.Entry; 

import javax.xml.bind.Marshaller; 
import javax.xml.bind.Unmarshaller; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlElement; 
import javax.xml.bind.annotation.XmlValue; 

public class AdaptedOptions extends Options { 

    @XmlAttribute String name; 
    @XmlElement List<Value> value = new ArrayList<Value>(); 
    Map<String, String> map = new HashMap<String, String>(); 

    public void beforeMarshal(Marshaller marshaller) { 
     for(Entry<String, String> entry : map.entrySet()) { 
      Value aValue = new Value(); 
      aValue.name = entry.getKey(); 
      aValue.value = entry.getValue(); 
      value.add(aValue); 
     } 
    } 

    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { 
     for(Value aValue : value) { 
      map.put(aValue.name, aValue.value); 
     } 
    } 

    private static class Value { 
     @XmlAttribute String name; 
     @XmlValue String value; 
    } 

} 
8

puede crear una clases separadas para representar la estructura de su XML:

public class Section { 
    @XmlAttribute 
    public String name; 
    @XmlElement(name = "value") 
    public List<Value> values; 
} 

public class Value { 
    @XmlAttribute 
    public String name; 
    @XmlValue 
    public String value; 
} 

y luego utilizar un XmlAdapter para realizar la conversión:

public class OptionsAdapter extends XmlAdapter<Section, Options> { 
    public Options unmarshal(Section s) { 
     if ("default_options".equals(s.name)) { 
      ... 
     } else if (...) { 
      ... 
     } 
     ... 
    } 
    ... 
} 

@XmlElement 
public class Configuration { 
    @XmlElement(name = "section") 
    @XmlJavaTypeAdapter(OptionsAdapter.class) 
    public List<Options> options; 
} 

public class DefaultOptions extends Options { ... } 
public class CustomOptions extends Options { ... } 
+0

Solución interesante. Primero de, gracias. Lo intentaré. ¿Es la única forma de resolver el problema? O tal vez hay soluciones un poco más elegantes sin "if" (default_options ".equals (s.name))"? –

+0

@Artyom: como JAXB no puede deducir la clase objetivo del valor del atributo, de todos modos requiere algún tipo de dipatching manual. Por supuesto, puedes hacerlo de forma más abstracta, p. usa un mapa de los valores de los atributos a las fábricas de opciones. – axtavt

Cuestiones relacionadas