Gson: How to exclude specific fields from Serialization without annotations

后端 未结 15 1432
后悔当初
后悔当初 2020-11-22 05:39

I\'m trying to learn Gson and I\'m struggling with field exclusion. Here are my classes

public class Student {    
  private Long                id;
  privat         


        
相关标签:
15条回答
  • 2020-11-22 06:21

    So, you want to exclude firstName and country.name. Here is what your ExclusionStrategy should look like

        public class TestExclStrat implements ExclusionStrategy {
    
            public boolean shouldSkipClass(Class<?> arg0) {
                return false;
            }
    
            public boolean shouldSkipField(FieldAttributes f) {
    
                return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
                (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
            }
    
        }
    

    If you see closely it returns true for Student.firstName and Country.name, which is what you want to exclude.

    You need to apply this ExclusionStrategy like this,

        Gson gson = new GsonBuilder()
            .setExclusionStrategies(new TestExclStrat())
            //.serializeNulls() <-- uncomment to serialize NULL fields as well
            .create();
        Student src = new Student();
        String json = gson.toJson(src);
        System.out.println(json);
    

    This returns:

    { "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}
    

    I assume the country object is initialized with id = 91L in student class.


    You may get fancy. For example, you do not want to serialize any field that contains "name" string in its name. Do this:

    public boolean shouldSkipField(FieldAttributes f) {
        return f.getName().toLowerCase().contains("name"); 
    }
    

    This will return:

    { "initials": "P.F", "country": { "id": 91 }}
    

    EDIT: Added more info as requested.

    This ExclusionStrategy will do the thing, but you need to pass "Fully Qualified Field Name". See below:

        public class TestExclStrat implements ExclusionStrategy {
    
            private Class<?> c;
            private String fieldName;
            public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
            {
                this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
                this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
            }
            public boolean shouldSkipClass(Class<?> arg0) {
                return false;
            }
    
            public boolean shouldSkipField(FieldAttributes f) {
    
                return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
            }
    
        }
    

    Here is how we can use it generically.

        Gson gson = new GsonBuilder()
            .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
            //.serializeNulls()
            .create();
        Student src = new Student();
        String json = gson.toJson(src);
        System.out.println(json); 
    

    It returns:

    { "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}
    
    0 讨论(0)
  • 2020-11-22 06:21

    After reading all available answers I found out, that most flexible, in my case, was to use custom @Exclude annotation. So, I implemented simple strategy for this (I didn't want to mark all fields using @Expose nor I wanted to use transient which conflicted with in app Serializable serialization) :

    Annotation:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface Exclude {
    }
    

    Strategy:

    public class AnnotationExclusionStrategy implements ExclusionStrategy {
    
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            return f.getAnnotation(Exclude.class) != null;
        }
    
        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            return false;
        }
    }
    

    Usage:

    new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
    
    0 讨论(0)
  • 2020-11-22 06:21

    This what I always use:

    The default behaviour implemented in Gson is that null object fields are ignored.

    Means Gson object does not serialize fields with null values to JSON. If a field in a Java object is null, Gson excludes it.

    You can use this function to convert some object to null or well set by your own

         /**
       * convert object to json
       */
      public String toJson(Object obj) {
        // Convert emtpy string and objects to null so we don't serialze them
        setEmtpyStringsAndObjectsToNull(obj);
        return gson.toJson(obj);
      }
    
      /**
       * Sets all empty strings and objects (all fields null) including sets to null.
       *
       * @param obj any object
       */
      public void setEmtpyStringsAndObjectsToNull(Object obj) {
        for (Field field : obj.getClass().getDeclaredFields()) {
          field.setAccessible(true);
          try {
            Object fieldObj = field.get(obj);
            if (fieldObj != null) {
              Class fieldType = field.getType();
              if (fieldType.isAssignableFrom(String.class)) {
                if(fieldObj.equals("")) {
                  field.set(obj, null);
                }
              } else if (fieldType.isAssignableFrom(Set.class)) {
                for (Object item : (Set) fieldObj) {
                  setEmtpyStringsAndObjectsToNull(item);
                }
                boolean setFielToNull = true;
                for (Object item : (Set) field.get(obj)) {
                  if(item != null) {
                    setFielToNull = false;
                    break;
                  }
                }
                if(setFielToNull) {
                  setFieldToNull(obj, field);
                }
              } else if (!isPrimitiveOrWrapper(fieldType)) {
                setEmtpyStringsAndObjectsToNull(fieldObj);
                boolean setFielToNull = true;
                for (Field f : fieldObj.getClass().getDeclaredFields()) {
                  f.setAccessible(true);
                  if(f.get(fieldObj) != null) {
                    setFielToNull = false;
                    break;
                  }
                }
                if(setFielToNull) {
                  setFieldToNull(obj, field);
                }
              }
            }
          } catch (IllegalAccessException e) {
            System.err.println("Error while setting empty string or object to null: " + e.getMessage());
          }
        }
      }
    
      private void setFieldToNull(Object obj, Field field) throws IllegalAccessException {
        if(!Modifier.isFinal(field.getModifiers())) {
          field.set(obj, null);
        }
      }
    
      private boolean isPrimitiveOrWrapper(Class fieldType)  {
        return fieldType.isPrimitive()
            || fieldType.isAssignableFrom(Integer.class)
            || fieldType.isAssignableFrom(Boolean.class)
            || fieldType.isAssignableFrom(Byte.class)
            || fieldType.isAssignableFrom(Character.class)
            || fieldType.isAssignableFrom(Float.class)
            || fieldType.isAssignableFrom(Long.class)
            || fieldType.isAssignableFrom(Double.class)
            || fieldType.isAssignableFrom(Short.class);
      }
    
    0 讨论(0)
  • 2020-11-22 06:23

    I used this strategy: i excluded every field which is not marked with @SerializedName annotation, i.e.:

    public class Dummy {
    
        @SerializedName("VisibleValue")
        final String visibleValue;
        final String hiddenValue;
    
        public Dummy(String visibleValue, String hiddenValue) {
            this.visibleValue = visibleValue;
            this.hiddenValue = hiddenValue;
        }
    }
    
    
    public class SerializedNameOnlyStrategy implements ExclusionStrategy {
    
        @Override
        public boolean shouldSkipField(FieldAttributes f) {
            return f.getAnnotation(SerializedName.class) == null;
        }
    
        @Override
        public boolean shouldSkipClass(Class<?> clazz) {
            return false;
        }
    }
    
    
    Gson gson = new GsonBuilder()
                    .setExclusionStrategies(new SerializedNameOnlyStrategy())
                    .create();
    
    Dummy dummy = new Dummy("I will see this","I will not see this");
    String json = gson.toJson(dummy);
    

    It returns

    {"VisibleValue":"I will see this"}

    0 讨论(0)
  • 2020-11-22 06:24

    Nishant provided a good solution, but there's an easier way. Simply mark the desired fields with the @Expose annotation, such as:

    @Expose private Long id;
    

    Leave out any fields that you do not want to serialize. Then just create your Gson object this way:

    Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
    
    0 讨论(0)
  • 2020-11-22 06:24

    You can explore the json tree with gson.

    Try something like this :

    gson.toJsonTree(student).getAsJsonObject()
    .get("country").getAsJsonObject().remove("name");
    

    You can add some properties also :

    gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);
    

    Tested with gson 2.2.4.

    0 讨论(0)
提交回复
热议问题