How can I get Gson to use accessors rather than fields?

后端 未结 2 951
再見小時候
再見小時候 2021-02-20 00:18

By default Gson uses fields as a basis for it\'s serialization. Is there a way to get it to use accessors instead?

2条回答
  •  情深已故
    2021-02-20 01:03

    The developers of Gson say that they never felt swayed by the requests to add this feature and they were worried about murkying up the api to add support for this.

    One way of adding this functionality is by using a TypeAdapter (I apologize for the gnarly code but this demonstrates the principle):

    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 extends TypeAdapter {
    
      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.");
      }
    }
    

    You can register this as a normal type adapter for a given type or through a TypeAdapterfactory - possibly checking for the presence of a runtime annotation:

    public class TypeFactory implements TypeAdapterFactory {
    
      @SuppressWarnings("unchecked")
      public  TypeAdapter create(final Gson gson, final TypeToken type) {
        Class t = type.getRawType();
        if(t.isAnnotationPresent(UseAccessor.class)) {
         return (TypeAdapter) new AccessorBasedTypeAdaptor(gson);
        }
        return null;
      }
    

    This can be specified as normal when creating your gson instance:

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

提交回复
热议问题