-
Notifications
You must be signed in to change notification settings - Fork 1
feat: support explictly null set in model(poc) #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: java-sdk-2.18.2-2025-01-06T07-06-06.523Z
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.smartx.tower; | ||
|
||
import com.google.gson.Gson; | ||
import com.smartx.tower.model.*; | ||
import java.io.IOException; | ||
|
||
public class App { | ||
public static void main(String[] args) throws ApiException, IOException { | ||
test(); | ||
} | ||
|
||
public static void test() throws ApiException, IOException { | ||
VmUpdateHostOptionsParams params = new VmUpdateHostOptionsParams(); | ||
VmUpdateHostOptionsParamsData data = new VmUpdateHostOptionsParamsData(); | ||
data.hostname(null).presentDnsServers(); | ||
params.setData(data); | ||
Gson gson = new JSON().getGson(); | ||
System.out.println(gson.toJson(data)); | ||
System.out.println(gson.toJson(params)); | ||
VmUpdateHostOptionsParamsData data2 = gson.fromJson("{\"dns_servers\":[\"114.114.114.114\"]}", | ||
VmUpdateHostOptionsParamsData.class); | ||
VmUpdateHostOptionsParams params2 = gson.fromJson("{\"data\":{\"dns_servers\":[\"114.114.114.114\"]}}", | ||
VmUpdateHostOptionsParams.class); | ||
System.out.println(data2); | ||
System.out.println(params2); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
package com.smartx.tower; | ||
|
||
import java.lang.reflect.Field; | ||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import com.google.gson.Gson; | ||
import com.google.gson.JsonElement; | ||
import com.google.gson.JsonObject; | ||
import com.google.gson.TypeAdapter; | ||
import com.google.gson.TypeAdapterFactory; | ||
import com.google.gson.annotations.SerializedName; | ||
import com.google.gson.reflect.TypeToken; | ||
import com.google.gson.stream.JsonReader; | ||
import com.google.gson.stream.JsonWriter; | ||
|
||
public class ConditionalNullable { | ||
private final static String IS_PRESENT = "_isPresent_"; | ||
|
||
public static class ConditionalNullablePojo { | ||
protected Set<String> _isPresent_ = new HashSet<String>(); | ||
} | ||
|
||
public static class ConditionalNullablePojoAdapterFactory implements TypeAdapterFactory { | ||
@Override | ||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { | ||
Class<? super T> rawType = type.getRawType(); | ||
if (!ConditionalNullablePojo.class.isAssignableFrom(rawType)) { | ||
return null; | ||
} | ||
Map<String, Field> jsonField = new HashMap<>(); | ||
Field[] fields = rawType.getDeclaredFields(); | ||
for (int i = 0; i < fields.length; i++) { | ||
Field field = fields[i]; | ||
if (field.isAnnotationPresent(SerializedName.class)) { | ||
jsonField.put(field.getAnnotation(SerializedName.class).value(), field); | ||
} | ||
} | ||
return new TypeAdapter<T>() { | ||
@Override | ||
public void write(JsonWriter out, T value) | ||
throws java.io.IOException { | ||
if (value == null) { | ||
out.nullValue(); | ||
return; | ||
} | ||
out.beginObject(); | ||
ConditionalNullablePojo pojo = (ConditionalNullablePojo) value; | ||
// TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class); | ||
for (Map.Entry<String, Field> entry : jsonField.entrySet()) { | ||
String fieldName = entry.getKey(); | ||
Field field = entry.getValue(); | ||
// get the value of the field | ||
Object fieldValue; | ||
try { | ||
// mostly the field is private, so we need to make it accessible | ||
if (!field.canAccess(value)) { | ||
field.setAccessible(true); | ||
} | ||
fieldValue = field.get(value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 为啥 private field 还需要被序列化?我看常见的做法是只处理 public getter/setter There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 我试了一下,发现 getter 来获取其实不太方便,因为 getter 本质还是一个 function,没法准确的通过是否以 get 开头就是 getter 的逻辑来推断一个方法一定是 getter。。。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 可以看看 beanutils getProperty() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. emmm,看了一下,是能解决如何推断 getter,但是我还需要 annotation 的 value orz There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不过我现在想我是不是该放弃反射,给每个类单独生成一个 Adapter |
||
} catch (IllegalArgumentException | IllegalAccessException e) { | ||
fieldValue = null; | ||
} | ||
if (fieldValue == null) { | ||
if (pojo._isPresent_.contains(fieldName)) { | ||
out.name(fieldName); | ||
if (!out.getSerializeNulls()) { | ||
out.setSerializeNulls(true); | ||
out.nullValue(); | ||
out.setSerializeNulls(false); | ||
} else { | ||
out.nullValue(); | ||
} | ||
} | ||
} else { | ||
// write the non null field with native adapter | ||
out.name(fieldName); | ||
@SuppressWarnings("unchecked") | ||
TypeAdapter<Object> delegatedAdapter = (TypeAdapter<Object>) gson | ||
.getAdapter(field.getType()); | ||
if (delegatedAdapter != null && fieldValue != null) { | ||
delegatedAdapter.write(out, fieldValue); | ||
} | ||
} | ||
} | ||
out.endObject(); | ||
} | ||
|
||
public T read(JsonReader in) throws java.io.IOException { | ||
JsonObject jsonObject = gson.getAdapter(JsonElement.class).read(in).getAsJsonObject(); | ||
T instance = gson.getDelegateAdapter(ConditionalNullablePojoAdapterFactory.this, type) | ||
.fromJsonTree(jsonObject); | ||
return instance; | ||
} | ||
}; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个
_isPresent_
是故意的吗,这个名字看着有点怪There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
避免和潜在的可能字段冲突,用 _ 当 prefix 过滤一层,在自己这边不在 api 参数中定义 _ 前缀的字段