특정 클래스 또는 필드에 대한 Gson serialize null입니다.
특정 필드 또는 클래스에 대해 null을 직렬화하고 싶습니다.
GSON에서는 옵션serializeNulls()JSON 전체에 적용됩니다.
예:
class MainClass {
public String id;
public String name;
public Test test;
}
class Test {
public String name;
public String value;
}
MainClass mainClass = new MainClass();
mainClass.id = "101"
// mainClass has no name.
Test test = new Test();
test.name = "testName";
test.value = null;
mainClass.test = test;
GSON을 사용한 JSON 작성:
GsonBuilder builder = new GsonBuilder().serializeNulls();
Gson gson = builder.create();
System.out.println(gson.toJson(mainClass));
현재 출력:
{
"id": "101",
"name": null,
"test": {
"name": "testName",
"value": null
}
}
원하는 출력:
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
원하는 출력을 얻는 방법
권장 솔루션은 다음과 같은 속성을 가집니다.
- 기본적으로는 null을 직렬화하지 않습니다.
- 특정 주석이 있는 필드에 대해 null을 직렬화합니다.
Aleksey와 유사한 솔루션을 가지고 있지만, 이 솔루션은 모든 클래스의 하나 이상의 필드에 적용할 수 있습니다(Kotlin의 예).
null로 일련화할 필드에 대한 새 주석을 만듭니다.
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD)
annotation class SerializeNull
작성하다TypeAdapterFactory클래스에 이 주석이 달린 필드가 있는지 확인하고 다음 필드를 삭제합니다.null의 주석으로 주석을 달지 않습니다.JsonTree오브젝트를 쓸 때:
class SerializableAsNullConverter : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
fun Field.serializedName() = declaredAnnotations
.filterIsInstance<SerializedName>()
.firstOrNull()?.value ?: name
val declaredFields = type.rawType.declaredFields
val nullableFieldNames = declaredFields
.filter { it.declaredAnnotations.filterIsInstance<SerializeNull>().isNotEmpty() }
.map { it.serializedName() }
val nonNullableFields = declaredFields.map { it.serializedName() } - nullableFieldNames
return if (nullableFieldNames.isEmpty()) {
null
} else object : TypeAdapter<T>() {
private val delegateAdapter = gson.getDelegateAdapter(this@SerializableAsNullConverter, type)
private val elementAdapter = gson.getAdapter(JsonElement::class.java)
override fun write(writer: JsonWriter, value: T?) {
val jsonObject = delegateAdapter.toJsonTree(value).asJsonObject
nonNullableFields
.filter { jsonObject.get(it) is JsonNull }
.forEach { jsonObject.remove(it) }
val originalSerializeNulls = writer.serializeNulls
writer.serializeNulls = true
elementAdapter.write(writer, jsonObject)
writer.serializeNulls = originalSerializeNulls
}
override fun read(reader: JsonReader): T {
return delegateAdapter.read(reader)
}
}
}
}
어댑터를 Gson 인스턴스에 등록합니다.
val builder = GsonBuilder().registerTypeAdapterFactory(SerializableAsNullConverter())
null로 할 필드에 주석을 추가합니다.
class MyClass(val id: String?, @SerializeNull val name: String?)
시리얼화 결과:
val myClass = MyClass(null, null)
val gson = builder.create()
val json = gson.toJson(myClass)
json:
{
"name": null
}
오브젝트를 null로 시리얼화하는 타이밍을 확인하는 인터페이스가 있습니다.
public interface JsonNullable {
boolean isJsonNull();
}
대응하는 타입 어댑터(쓰기 전용)
public class JsonNullableAdapter extends TypeAdapter<JsonNullable> {
final TypeAdapter<JsonElement> elementAdapter = new Gson().getAdapter(JsonElement.class);
final TypeAdapter<Object> objectAdapter = new Gson().getAdapter(Object.class);
@Override
public void write(JsonWriter out, JsonNullable value) throws IOException {
if (value == null || value.isJsonNull()) {
//if the writer was not allowed to write null values
//do it only for this field
if (!out.getSerializeNulls()) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.nullValue();
}
} else {
JsonElement tree = objectAdapter.toJsonTree(value);
elementAdapter.write(out, tree);
}
}
@Override
public JsonNullable read(JsonReader in) throws IOException {
return null;
}
}
다음과 같이 사용합니다.
public class Foo implements JsonNullable {
@Override
public boolean isJsonNull() {
// You decide
}
}
Foo 값을 null로 일련화할 필요가 있는 클래스입니다.foo 값 자체는 null이 아니어야 합니다.그렇지 않으면 커스텀어댑터 주석이 무시됩니다.
public class Bar {
@JsonAdapter(JsonNullableAdapter.class)
public Foo foo = new Foo();
}
Java 버전의 @Joris의 뛰어난 답변을 원하는 사용자에게는 아래 코드가 도움이 될 것입니다.이는 대부분 Kotlin의 번역일 뿐이며, Atribute의 Serialized Name이 Atribute Name과 다를 때 항상 동작하도록 Atribute의 Serialised Name을 가져오는 방법을 약간 개선했습니다(원래 답변의 코멘트 참조).
이거는TypeAdapterFactory구현:
public class NullableAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFieldNames = new ArrayList<>();
List<String> nonNullableFieldNames = new ArrayList<>();
for (Field declaredField : declaredFields) {
if (declaredField.isAnnotationPresent(JsonNullable.class)) {
if (declaredField.getAnnotation(SerializedName.class) != null) {
nullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
} else {
nullableFieldNames.add(declaredField.getName());
}
} else {
if (declaredField.getAnnotation(SerializedName.class) != null) {
nonNullableFieldNames.add(declaredField.getAnnotation(SerializedName.class).value());
} else {
nonNullableFieldNames.add(declaredField.getName());
}
}
}
if (nullableFieldNames.size() == 0) {
return null;
}
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(NullableAdapterFactory.this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
for (String name: nonNullableFieldNames) {
if (jsonObject.has(name) && jsonObject.get(name) instanceof JsonNull) {
jsonObject.remove(name);
}
}
boolean originalSerializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
out.setSerializeNulls(originalSerializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
그리고 이거는@JsonNullable주석을 사용하여 대상 속성을 표시합니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JsonNullable {
}
나는 그것을 실장했다.@JsonAdapter(NullableAdapterFactory.class)오브젝트 클래스에 대한 주석, 대신 오브젝트 클래스로 등록합니다.TypeAdapterFactory에서GsonBuilder인스턴스(instance)에서 오브젝트 클래스는 다음과 같습니다.
@JsonAdapter(NullableAdapterFactory.class)
public class Person {
public String firstName;
public String lastName;
@JsonNullable
public String someNullableInfo;
}
다만, 다른 어프로치는, 필요에 따라서 이 코드에 대해서도 유효하게 동작합니다.
서브클래스를 만듭니다.com.google.gson.TypeAdapter주석을 사용하여 필수 필드에 등록합니다.com.google.gson.annotations.JsonAdapter또는 다음 방법으로 등록합니다.GsonBuilder.registerTypeAdapter그 어댑터로write(그리고read)를 실장할 필요가 있습니다.예를 들어 다음과 같습니다.
public class JsonTestNullableAdapter extends TypeAdapter<Test> {
@Override
public void write(JsonWriter out, Test value) throws IOException {
out.beginObject();
out.name("name");
out.value(value.name);
out.name("value");
if (value.value == null) {
out.setSerializeNulls(true);
out.nullValue();
out.setSerializeNulls(false);
} else {
out.value(value.value);
}
out.endObject();
}
@Override
public Test read(JsonReader in) throws IOException {
in.beginObject();
Test result = new Test();
in.nextName();
if (in.peek() != NULL) {
result.name = in.nextString();
} else {
in.nextNull();
}
in.nextName();
if (in.peek() != NULL) {
result.value = in.nextString();
} else {
in.nextNull();
}
in.endObject();
return result;
}
}
MainClassJsonAdapter의 " " "에 대한 주석Test다음 중 하나:
public static class MClass {
public String id;
public String name;
@JsonAdapter(JsonTestNullableAdapter.class)
public Test test;
}
의 System.out.println(new Gson.toJson(mainClass)) 말합니다
{
"id": "101",
"test": {
"name": "testName",
"value": null
}
}
저는 여기서 여러 가지 답변에서 아이디어를 얻었습니다.
이 실장:
- 실행 시 JSON이 다음 상태인지 여부를 선택할 수 있습니다.
- 무효
- happens happens happens
JsonNullable.isJsonNull() == true
- happens happens happens
- 무효
- happens happens happens
JsonNullable.isJsonNull() == false
- happens happens happens
- JSON(HTTP PATCH)
- 는 " " " 입니다.
ParentJsonNullablenull
- 는 " " " 입니다.
- 무효
- 주석이 필요 없음
- 하지 않은
delegateAdapter를TypeAdapterFactory
이 인터페이스를 null로 구현하기 위해 시리얼화해야 할 수 있는 오브젝트
/**
* [JsonNullableTypeAdapterFactory] needs to be registered with the [com.google.gson.Gson]
* serializing implementations of [JsonNullable] for [JsonNullable] to work.
*
* [JsonNullable] allows objects to choose at runtime whether they should be serialized as "null"
* serialized normally, or be omitted from the JSON output from [com.google.gson.Gson].
*
* when [isJsonNull] returns true, the subclass will be serialized to a [com.google.gson.JsonNull].
*
* when [isJsonNull] returns false, the subclass will be serialized normally.
*/
interface JsonNullable {
/**
* return true to have the entire object serialized as `null` during JSON serialization.
* return false to have this object serialized normally.
*/
fun isJsonNull(): Boolean
}
값을 null로 직렬화하는 어댑터 팩토리 유형
class JsonNullableTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
return object : TypeAdapter<T>() {
private val delegateAdapter = gson.getDelegateAdapter(this@JsonNullableTypeAdapterFactory, type)
override fun read(reader: JsonReader): T = delegateAdapter.read(reader)
override fun write(writer: JsonWriter, value: T?) {
if (value is JsonNullable && value.isJsonNull()) {
val originalSerializeNulls = writer.serializeNulls
writer.serializeNulls = true
writer.nullValue()
writer.serializeNulls = originalSerializeNulls
} else {
delegateAdapter.write(writer, value)
}
}
}
}
}
어댑터 팩트로이를 GSON에 등록합니다.
new GsonBuilder()
// ....
.registerTypeAdapterFactory(new JsonNullableTypeAdapterFactory())
// ....
.create();
JSON에 직렬화되는 예제 개체
data class Parent(
val hello: Child?,
val world: Child?
)
data class Child(
val name: String?
) : JsonNullable {
override fun isJsonNull(): Boolean = name == null
}
@Arvoreniad의 답변에 추가
2개의 추가는 출력에 true로 설정한 후 JsonWriter의 늘시리얼라이제이션 상태를 리셋하고 필드명을 취득하기 위해 Gson의 필드명 정책을 사용합니다.
public class SerializeNullTypeAdapterFactory implements TypeAdapterFactory {
/**
* {@inheritDoc}
*/
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Field[] declaredFields = type.getRawType().getDeclaredFields();
List<String> nullableFields = new ArrayList<>();
List<String> nonNullableFields = new ArrayList<>();
FieldNamingStrategy fieldNamingStrategy = gson.fieldNamingStrategy();
for (Field declaredField : declaredFields) {
// The Gson FieldNamingStrategy will handle the @SerializedName annotation + casing conversions
final String fieldName = fieldNamingStrategy.translateName(declaredField);
if (declaredField.isAnnotationPresent(JsonNullable.class)) {
nullableFields.add(fieldName);
} else {
nonNullableFields.add(fieldName);
}
}
if (nullableFields.isEmpty()) {
return null;
}
TypeAdapter<T> delegateAdapter = gson.getDelegateAdapter(this, type);
TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class);
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
JsonObject jsonObject = delegateAdapter.toJsonTree(value).getAsJsonObject();
nonNullableFields.forEach((var name) -> {
if (jsonObject.has(name) && (jsonObject.get(name) instanceof JsonNull)) {
jsonObject.remove(name);
}
});
boolean serializeNulls = out.getSerializeNulls();
out.setSerializeNulls(true);
elementAdapter.write(out, jsonObject);
// Reset default (in case JsonWriter is reused)
out.setSerializeNulls(serializeNulls);
}
@Override
public T read(JsonReader in) throws IOException {
return delegateAdapter.read(in);
}
};
}
}
언급URL : https://stackoverflow.com/questions/35477267/gson-serialize-null-for-specific-class-or-field
'programing' 카테고리의 다른 글
| CRUDRepository에서 업데이트 또는 저장 업데이트 (0) | 2023.03.31 |
|---|---|
| Robo 3T를 사용하여 MongoDB에서 JSON을 내보내는 방법 (0) | 2023.03.31 |
| 업데이트 압축을 푸는 중...디렉토리를 작성할 수 없습니다.워드프레스 (0) | 2023.03.31 |
| AngularJS 응용 프로그램 파일 구조 (0) | 2023.03.31 |
| Angular JS: 약속에 구속하는 방법 (0) | 2023.03.31 |