programing

[ Gson Serialize ]필드는 늘이 아니거나 공백이 아닌 경우에만

instargram 2023. 4. 2. 09:44
반응형

[ Gson Serialize ]필드는 늘이 아니거나 공백이 아닌 경우에만

java 객체를 json으로 변환해야 하는 요건이 있습니다.

Gson을 사용하고 있는데 null이 아닌 값이나 비어 있지 않은 값만 시리얼화하려면 변환기가 필요합니다.

예를 들어 다음과 같습니다.

//my java object looks like
class TestObject{
    String test1;
    String test2;
    OtherObject otherObject = new OtherObject();
}

이 개체를 json으로 변환하는 Gson 인스턴스는 다음과 같습니다.

Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";

String jsonStr = gson.toJson(obj);
println jsonStr;

위의 인쇄에서 결과는 다음과 같습니다.

{"test1":"test1", "test2":"", "otherObject":{}}

여기서 난 그저 결과가 좋기를 바랐다.

{"test1":"test1"}

test2가 비어 있고 otherObject가 비어 있기 때문에 json 데이터로 시리얼화하지 않았으면 합니다.

참고로 저는 Groovy/Grails를 사용하고 있기 때문에 이 플러그인이 있다면 gson serialization class를 커스터마이즈하는 것이 좋을 것 같습니다.

독자적인 작성TypeAdapter

public class MyTypeAdapter extends TypeAdapter<TestObject>() {

    @Override
    public void write(JsonWriter out, TestObject value) throws IOException {
        out.beginObject();
        if (!Strings.isNullOrEmpty(value.test1)) {
            out.name("test1");
            out.value(value.test1);
        }

        if (!Strings.isNullOrEmpty(value.test2)) {
            out.name("test2");
            out.value(value.test1);
        }
        /* similar check for otherObject */         
        out.endObject();    
    }

    @Override
    public TestObject read(JsonReader in) throws IOException {
        // do something similar, but the other way around
    }
}

그런 다음 에 등록할 수 있습니다.Gson.

Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));

생산하다

 {"test1":"test1"}

GsonBuilder클래스에는 독자적인 시리얼화/디시리얼화 스트래티지 작성, 타입 어댑터 등록 및 기타 파라미터 설정을 위한 다양한 메서드가 있습니다.

Strings과바 클래스입니다.그런 종속성을 원하지 않으면 직접 확인할 수 있습니다.

제가 개인적으로 싫어하는 건TypeAdapter50개의 필드(50개의 필드, 즉 50개의 필드)를 가질 수 있는 전체 클래스의 모든 필드를 설명할 필요가 있다는 사실을 사용합니다.if블록 인TypeAdapter).
저의 솔루션은Reflection그리고 사실Gson는 디폴트로는 늘 값 필드를 시리얼화하지 않습니다.
DocumentModel이라고 하는 API용 데이터를 보관하고 있는 특별한 클래스가 있습니다.이 클래스는 50개 정도의 필드가 있으며 전송은 좋아하지 않습니다.String" (비어 있지만 null이 아님) 값이 있는 필드 또는 빈 배열이 서버에 표시됩니다.그래서 빈 필드를 모두 무효로 하고 오브젝트의 복사본을 반환하는 특별한 메서드를 만들었습니다.주의 - 기본적으로는 DocumentModel 인스턴스의 모든 어레이는 빈 (0길이) 어레이로 초기화되므로 늘이 아닙니다.아마도 길이를 체크하기 전에 어레이의 늘을 확인해야 합니다.

public DocumentModel getSerializableCopy() {
    Field fields[] = new Field[]{};
    try {
        // returns the array of Field objects representing the public fields
        fields = DocumentModel.class.getDeclaredFields();
    } catch (Exception e) {
        e.printStackTrace();
    }
    DocumentModel copy = new DocumentModel();
    Object value;
    for (Field field : fields) {
        try {
            value = field.get(this);
            if (value instanceof String && TextUtils.isEmpty((String) value)) {
                field.set(copy, null);
            // note: here array is not being checked for null!
            else if (value instanceof Object[] && ((Object[]) value).length == 0) {
                field.set(copy, null);
            } else
                field.set(copy, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    return copy;
}

이 방법을 사용하면 이 방법을 쓴 후 일부 필드가 추가 또는 제거되었든 상관 없습니다.남은 유일한 문제는 커스텀타입 필드를 체크하는 것입니다.이 필드는 체크되지 않습니다.String또는 배열이지만 이는 특정 클래스에 따라 다르며 if/clocks에서 추가 코딩해야 합니다.

내가 보기에 문제는 gson이 아닌 것 같다.Gson은 null 문자열과 빈 문자열의 차이를 올바르게 추적합니다.이 구별을 지우시겠습니까?TestObject를 사용하는 모든 클래스가 상관없습니다.

차이를 신경 쓰지 않는 경우 TestObject 내에서 빈 문자열을 null로 변경한 후 시리얼화합니다.또는 빈 문자열이 null로 설정되도록 TestObject의 setters를 만듭니다.그러면 빈 문자열이 null과 동일하도록 클래스 내에서 엄격하게 정의할 수 있습니다.설정기 밖에서 값을 설정할 수 없는지 확인해야 합니다.

같은 문제에 부딪혀 두 가지 다른 해결책을 찾았습니다.


  1. 각 필드 클래스에 대해 사용자 지정 유형 어댑터 작성

String 클래스의 TypeAdapter 예제:

@SuppressWarnings("rawtypes")
public class JSONStringAdapter extends TypeAdapter {

    @Override
    public String read(JsonReader jsonReader) throws IOException {
            
        String value = jsonReader.nextString();
        if(value == null || value.trim().length() == 0) {
            return null;
        } else {
            return value;
        }
    }

    @Override
    public void write(JsonWriter jsonWriter, Object object) throws IOException {
    
        String value = String.valueOf(object);
        if(value == null || value.trim().length() == 0) {    
            jsonWriter.nullValue();
        } else {
            jsonWriter.value(value);
       }
    }
}

용도:

public class Doggo {

    @JsonAdapter(JSONStringAdapter.class)
    private String name;

    public Doggo(String name) {
        this.name = name;
    }
}

public class Main {

    public static void main(String[] args) {
        Doggo aDoggo = new Doggo("");
        String jsonString = new Gson().toJson(aDoggo);
    }
}    

  1. JSON 문자열을 생성하기 전에 개체를 수동으로 처리합니다.

동작하고 있는 것 같으며, 퍼포먼스를 테스트하지 않았습니다.

public static boolean removeEmpty(JSONObject source) {
    
    if (null == source || source.length() == 0) {
        return true;
    }
    
    boolean isJsonObjectEmpty = false; 

    for (String key : JSONObject.getNames(source)) {
        Object value = source.get(key);
        
        boolean isValueEmpty = isValueEmpty(value);
        if(isValueEmpty) {
            source.remove(key);
        }
    }
    
    if(source.length() == 0) {
        isJsonObjectEmpty = true;
    }
    
    return isJsonObjectEmpty;
}

private static boolean isValueEmpty(Object value) {

    if (null == value) {
        return true;
    }
    
    if (value instanceof JSONArray) {
        
        JSONArray arr =  (JSONArray) value;
        if(arr.length() > 0) {
            
            List<Integer> indextesToRemove = new ArrayList<>();
            for(int i = 0; i< arr.length(); i++) {
                
                boolean isValueEmpty = isValueEmpty(arr.get(i));
                if(isValueEmpty) {
                    indextesToRemove.add(i);
                };
            }
            
            for(Integer index : indextesToRemove) {
                arr.remove(index);
            }
            
            if(arr.length() == 0) {
                return true;
            }
            
        } else {
            return true;
        }
        
    } else if (value instanceof JSONObject) {
    
        return removeEmpty((JSONObject) value);
         
    } else {
        
        if (JSONObject.NULL.equals(value) 
            || null == value 
            || value.toString().trim().length() == 0)
        ) {
            return true;
        }
    }
    
    return false;
}

용도:

public class Doggo {

    private String name;

    public Doggo(String name) {
        this.name = name;
    }
}


public class Main {

    public static void main(String[] args) {
        Doggo aDoggo = new Doggo("");
        
        // if you are not using Type Adapters for your fields
        JSONObject aJSONObject1 = new JSONObject(aDoggo);
        removeEmpty(aJSONObject1);
        String jsonString1 = aJSONObject1.toString();

        // if you are using Type Adapters for your fields
        Gson gsonParser = new Gson();
        JSONObject aJSONObject2 = new JSONObject(gsonParser .toJson(aDoggo));
        removeEmpty(aJSONObject2);
        String jsonString2 = aJSONObject2.toString();
    }
}        

언급URL : https://stackoverflow.com/questions/18491733/gson-serialize-field-only-if-not-null-or-not-empty

반응형