Per impostazione predefinita Gson utilizza i campi come base per la sua serializzazione. C'è un modo per farlo usare invece gli accessor?Come posso ottenere Gson per usare accessors piuttosto che campi?
risposta
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();
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(). –
@ plasma147 Grazie per questo! Ecco l'annotazione UseAccessor: '@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.TYPE) @interface pubblico UseAccessor { }' –
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
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
- 1. Come posso usare linq per ordinare per più campi?
- 2. Posso definire un __repr__ per una classe piuttosto che un'istanza?
- 3. GSON come ottenere nome serializzato
- 4. Stenografi Accessors and Mutators
- 5. javascript accessors
- 6. Numero intero GSON a Boolean per campi specifici
- 7. Come posso usare gulp per creare le mappe in formato dattiloscritto in file diversi piuttosto che nei file javascript?
- 8. Usare Gson per gestire elegantemente oggetti JSON annidati?
- 9. Più GSON @SerializedName per campo?
- 10. Perché Object.defineProperty() piuttosto che this.defineProperty() (per oggetti)?
- 11. Accessors vs membri pubblici
- 12. In API Bloomberg come si specifica per ottenere FX forward come spread piuttosto che valori assoluti?
- 13. Posso importare i moduli babel-polyfill piuttosto che tutti?
- 14. apportate non GSON messo virgolette i nomi dei campi
- 15. Come posso usare parseInt per un doppio?
- 16. Perché non dovrei usare Child.prototype = Parent.Prototype piuttosto che Child.prototype = new Parent(); per l'ereditarietà di Javascript?
- 17. Come utilizzare un'istanza di PropertyDescriptor come parte di una definizione di Binding, piuttosto che usare PropertyPath?
- 18. Come ottenere aspettarsi -c per lavorare in un'unica linea, piuttosto che lo script
- 19. django forms esclude i campi su init piuttosto che sulla meta classe
- 20. Android Wear: c'è qualche motivo per usare un oggetto Time piuttosto che un oggetto Calendar?
- 21. CASO (Contiene) piuttosto che la dichiarazione uguale
- 22. Convenzioni Java per dati accessibili. (Accessors pubblici & Getter/Denominazione)
- 23. Come usare ransack sui campi delegati
- 24. Come posso ottenere campi utilizzati in un metodo (.NET)?
- 25. Come posso usare conda per installare pydot?
- 26. Posso usare OverlappingInstances per ottenere messaggi di errore più gradevoli?
- 27. Che cos'è phtml e quando dovrei usare un'estensione .phtml piuttosto che .php?
- 28. Come ottenere una pagina di benvenuto da Tomcat root, piuttosto che contesto webapp?
- 29. Come posso usare quel codice per usare LINQ
- 30. di riferimento con VM, piuttosto che $ portata
Come è ambigua, vaga, incomplete, troppo ampia, o retorico? Copre un caso d'uso molto comune. – plasma147