2012-07-08 10 views

risposta

7

Gli sviluppatori di Gson say che non si sono mai sentiti influenzati dalle richieste di aggiungere questa funzione e che erano preoccupati di oscurare l'API per aggiungere supporto per questo.

Un modo per aggiungere questa funzionalità è quello di utilizzare un TypeAdapter (mi scuso per il codice nodoso, ma questo dimostra il 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."); 
    } 
} 

È possibile registrare questo come un adattatore di tipo normale per un determinato tipo o attraverso un TypeAdapterfactory - forse il controllo per la presenza di un'annotazione runtime:

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

Questo può essere specificato come normale quando si crea l'istanza GSON:

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

Esempio brillante.Se in effetti si procede in tal senso, è possibile renderlo leggermente più efficiente cercando e memorizzando nella cache i metodi in TypeAdapterFactory.create(). –

+0

@ plasma147 Grazie per questo! Ecco l'annotazione UseAccessor: '@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @interface pubblico UseAccessor { }' –

+0

Grazie! Dio mi ha salvato un sacco di ulteriori problemi. Perché non c'è un esempio ufficiale di ciò che hai scritto? E grazie Smoky per l'annotazione UseAccessor, piuttosto nuova per Java, quindi mi avrebbe causato ancora più problemi nel tentativo di capire la parte di annotazione. – Whyser

2

Nota: Sono in vantaggio EclipseLink JAXB (MOXy) e un membro del gruppo di esperti JAXB (JSR-222).

Se non è possibile ottenere GSON a fare quello che vuoi, qui di seguito è come si può fare questo usando JSON nativa di moxy vincolante. MOXy come qualsiasi implementazione JAXB utilizzerà l'accesso di proprietà (pubblico) per impostazione predefinita. È possibile configurare l'accesso al campo utilizzando @XmlAccessorType(XmlAccessType.FIELD). Di seguito è riportato un esempio:

clienti

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

} 

Indirizzo

package forum11385214; 

public class Address { 

    private String foo; 

    public String getStreet() { 
     return foo; 
    } 

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

} 

jaxb.properties

Per configurare moxy come provider JAXB è necessario aggiungere un file chiamato jaxb.properties nello stesso pacchetto del modello di dominio con la seguente voce (vedere: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).

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

Demo

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/Output

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

Per ulteriori informazioni

Problemi correlati