2012-07-08 8 views

Respuesta

7

Los desarrolladores de Gson say que nunca se sintieron influenciados por las solicitudes para agregar esta característica y estaban preocupados por oscurecer la API para agregar soporte para esto.

Una forma de añadir esta funcionalidad es mediante el uso de un TypeAdapter (Me disculpo por el código retorcido pero esto demuestra el principio):

import java.io.IOException; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.reflect.InvocationTargetException; 
import java.lang.reflect.Method; 

import com.google.common.base.CaseFormat; 
import com.google.gson.Gson; 
import com.google.gson.TypeAdapter; 
import com.google.gson.reflect.TypeToken; 
import com.google.gson.stream.JsonReader; 
import com.google.gson.stream.JsonWriter; 

public class AccessorBasedTypeAdaptor<T> extends TypeAdapter<T> { 

    private Gson gson; 

    public AccessorBasedTypeAdaptor(Gson gson) { 
    this.gson = gson; 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public void write(JsonWriter out, T value) throws IOException { 
    out.beginObject(); 
    for (Method method : value.getClass().getMethods()) { 
     boolean nonBooleanAccessor = method.getName().startsWith("get"); 
     boolean booleanAccessor = method.getName().startsWith("is"); 
     if ((nonBooleanAccessor || booleanAccessor) && !method.getName().equals("getClass") && method.getParameterTypes().length == 0) { 
     try { 
      String name = method.getName().substring(nonBooleanAccessor ? 3 : 2); 
      name = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, name); 
      Object returnValue = method.invoke(value); 
      if(returnValue != null) { 
      TypeToken<?> token = TypeToken.get(returnValue.getClass()); 
      TypeAdapter adapter = gson.getAdapter(token); 
      out.name(name); 
      adapter.write(out, returnValue); 
      } 
     } catch (Exception e) { 
      throw new ConfigurationException("problem writing json: ", e); 
     } 
     } 
    } 
    out.endObject(); 
    } 

    @Override 
    public T read(JsonReader in) throws IOException { 
    throw new UnsupportedOperationException("Only supports writes."); 
    } 
} 

Puede registrar esto como un adaptador de tipo normal para un tipo dado o a través de un TypeAdapterfactory - posiblemente la comprobación de la presencia de una anotación de tiempo de ejecución:

public class TypeFactory implements TypeAdapterFactory { 

    @SuppressWarnings("unchecked") 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) { 
    Class<? super T> t = type.getRawType(); 
    if(t.isAnnotationPresent(UseAccessor.class)) { 
    return (TypeAdapter<T>) new AccessorBasedTypeAdaptor(gson); 
    } 
    return null; 
    } 

Esto se puede especificar como normal cuando la creación de la instancia de GSON:

new GsonBuilder().registerTypeAdapterFactory(new TypeFactory()).create(); 
+0

Brillante ejemplo.Si realmente avanzas con esto, probablemente puedas hacer que sea un poco más eficiente al buscar y almacenar en caché los métodos en TypeAdapterFactory.create(). –

+0

@ plasma147 ¡Gracias por esto! Aquí está la anotación UseAccessor: '@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) public @interface UseAccessor { }' –

+0

¡Gracias! Dios, esto me ahorró muchos más problemas. ¿Por qué no hay ningún ejemplo oficial de lo que escribiste? Y gracias a Smoky por el UseAccessor-annotation, bastante nuevo en Java, me habría causado incluso más problemas al tratar de descubrir la parte de la anotación. – Whyser

2

Nota: Soy el EclipseLink JAXB (MOXy) de plomo y un miembro del grupo de expertos JAXB (JSR-222).

Si no puede hacer que Gson haga lo que desea, a continuación se muestra cómo puede lograr esto utilizando el enlace JSON nativo de MOXy. MOXy, como cualquier implementación de JAXB, usará acceso de propiedad (pública) de forma predeterminada. Puede configurar el acceso al campo usando @XmlAccessorType(XmlAccessType.FIELD). A continuación se muestra un ejemplo:

cliente

package forum11385214; 

public class Customer { 

    private String foo; 
    private Address bar; 

    public String getName() { 
     return foo; 
    } 

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

    public Address getAddress() { 
     return bar; 
    } 

    public void setAddress(Address address) { 
     this.bar = address; 
    } 

} 

Dirección

package forum11385214; 

public class Address { 

    private String foo; 

    public String getStreet() { 
     return foo; 
    } 

    public void setStreet(String street) { 
     this.foo = street; 
    } 

} 

jaxb.properties

Para configurar moxy como su proveedor de JAXB es necesario agregar una archivo llamado jaxb.properties en el mismo paquete que su modelo de dominio con la siguiente entrada (consulte: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

demostración

package forum11385214; 

import java.util.*; 
import javax.xml.bind.*; 
import javax.xml.transform.stream.StreamSource; 
import org.eclipse.persistence.jaxb.JAXBContextProperties; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     Map<String, Object> properties = new HashMap<String, Object>(2); 
     properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json"); 
     properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false); 
     JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties); 

     Unmarshaller unmarshaller = jc.createUnmarshaller(); 
     StreamSource json = new StreamSource("src/forum11385214/input.json"); 
     Customer customer = (Customer) unmarshaller.unmarshal(json, Customer.class).getValue(); 

     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(customer, System.out); 
    } 

} 

input.json/salida

{ 
    "name" : "Jane Doe", 
    "address" : { 
     "street" : "1 Any Street" 
    } 
} 

Para más información

Cuestiones relacionadas