Skip to content

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

Draft
wants to merge 1 commit into
base: java-sdk-2.18.2-2025-01-06T07-06-06.523Z
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/main/java/com/smartx/tower/App.java
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);
}
}
100 changes: 100 additions & 0 deletions src/main/java/com/smartx/tower/ConditionalNullable.java
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>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 _isPresent_ 是故意的吗,这个名字看着有点怪

Copy link
Collaborator Author

@Sczlog Sczlog Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

避免和潜在的可能字段冲突,用 _ 当 prefix 过滤一层,在自己这边不在 api 参数中定义 _ 前缀的字段

}

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

为啥 private field 还需要被序列化?我看常见的做法是只处理 public getter/setter

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我试了一下,发现 getter 来获取其实不太方便,因为 getter 本质还是一个 function,没法准确的通过是否以 get 开头就是 getter 的逻辑来推断一个方法一定是 getter。。。
感觉通过 private field + annotation 判断的方法更加稳妥一点。

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以看看 beanutils getProperty()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emmm,看了一下,是能解决如何推断 getter,但是我还需要 annotation 的 value orz

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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;
}
};
}
}
}
39 changes: 23 additions & 16 deletions src/main/java/com/smartx/tower/JSON.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public class JSON {

@SuppressWarnings("unchecked")
public static GsonBuilder createGson() {
GsonFireBuilder fireBuilder = new GsonFireBuilder()
;
GsonFireBuilder fireBuilder = new GsonFireBuilder();
GsonBuilder builder = fireBuilder.createGsonBuilder();
return builder;
}
Expand All @@ -54,10 +53,13 @@ private static String getDiscriminatorValue(JsonElement readElement, String disc
}

/**
* Returns the Java class that implements the OpenAPI schema for the specified discriminator value.
* Returns the Java class that implements the OpenAPI schema for the specified
* discriminator value.
*
* @param classByDiscriminatorValue The map of discriminator values to Java classes.
* @param discriminatorValue The value of the OpenAPI discriminator in the input data.
* @param classByDiscriminatorValue The map of discriminator values to Java
* classes.
* @param discriminatorValue The value of the OpenAPI discriminator in
* the input data.
* @return The Java class that implements the OpenAPI schema
*/
private static Class getClassByDiscriminator(Map classByDiscriminatorValue, String discriminatorValue) {
Expand All @@ -70,12 +72,13 @@ private static Class getClassByDiscriminator(Map classByDiscriminatorValue, Stri

public JSON() {
gson = createGson()
.registerTypeAdapter(Date.class, dateTypeAdapter)
.registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter)
.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter)
.registerTypeAdapter(LocalDate.class, localDateTypeAdapter)
.registerTypeAdapter(byte[].class, byteArrayAdapter)
.create();
.registerTypeAdapter(Date.class, dateTypeAdapter)
.registerTypeAdapter(java.sql.Date.class, sqlDateTypeAdapter)
.registerTypeAdapter(OffsetDateTime.class, offsetDateTimeTypeAdapter)
.registerTypeAdapter(LocalDate.class, localDateTypeAdapter)
.registerTypeAdapter(byte[].class, byteArrayAdapter)
.registerTypeAdapterFactory(new ConditionalNullable.ConditionalNullablePojoAdapterFactory())
.create();
}

/**
Expand Down Expand Up @@ -103,7 +106,8 @@ public JSON setGson(Gson gson) {
*
* @param lenientOnJson Set it to true to ignore some syntax errors
* @return JSON
* @see <a href="https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html">https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html</a>
* @see <a href=
* "https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html">https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html</a>
*/
public JSON setLenientOnJson(boolean lenientOnJson) {
isLenientOnJson = lenientOnJson;
Expand Down Expand Up @@ -133,7 +137,8 @@ public <T> T deserialize(String body, Type returnType) {
try {
if (isLenientOnJson) {
JsonReader jsonReader = new JsonReader(new StringReader(body));
// see https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html
// see
// https://www.javadoc.io/doc/com.google.code.gson/gson/2.8.5/com/google/gson/stream/JsonReader.html
jsonReader.setLenient(true);
return gson.fromJson(jsonReader, returnType);
} else {
Expand Down Expand Up @@ -215,7 +220,7 @@ public OffsetDateTime read(JsonReader in) throws IOException {
default:
String date = in.nextString();
if (date.endsWith("+0000")) {
date = date.substring(0, date.length()-5) + "Z";
date = date.substring(0, date.length() - 5) + "Z";
}
return OffsetDateTime.parse(date, formatter);
}
Expand Down Expand Up @@ -282,7 +287,8 @@ public static class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {

private DateFormat dateFormat;

public SqlDateTypeAdapter() {}
public SqlDateTypeAdapter() {
}

public SqlDateTypeAdapter(DateFormat dateFormat) {
this.dateFormat = dateFormat;
Expand Down Expand Up @@ -335,7 +341,8 @@ public static class DateTypeAdapter extends TypeAdapter<Date> {

private DateFormat dateFormat;

public DateTypeAdapter() {}
public DateTypeAdapter() {
}

public DateTypeAdapter(DateFormat dateFormat) {
this.dateFormat = dateFormat;
Expand Down
25 changes: 11 additions & 14 deletions src/main/java/com/smartx/tower/model/VmUpdateHostOptionsParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.google.gson.annotations.SerializedName;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.smartx.tower.ConditionalNullable;
import com.smartx.tower.model.VmUpdateHostOptionsParamsData;
import com.smartx.tower.model.VmWhereInput;
import io.swagger.annotations.ApiModel;
Expand All @@ -17,7 +18,7 @@
* VmUpdateHostOptionsParams
*/
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaSmartxClientCodegen")
public class VmUpdateHostOptionsParams {
public class VmUpdateHostOptionsParams extends ConditionalNullable.ConditionalNullablePojo {
public static final String SERIALIZED_NAME_DATA = "data";
@SerializedName(SERIALIZED_NAME_DATA)
private VmUpdateHostOptionsParamsData data;
Expand All @@ -26,55 +27,53 @@ public class VmUpdateHostOptionsParams {
@SerializedName(SERIALIZED_NAME_WHERE)
private VmWhereInput where;

public VmUpdateHostOptionsParams() {
public VmUpdateHostOptionsParams() {
}

public VmUpdateHostOptionsParams data(VmUpdateHostOptionsParamsData data) {

this.data = data;
return this;
}

/**
/**
* Get data
*
* @return data
**/
**/
@javax.annotation.Nonnull
@ApiModelProperty(required = true, value = "")

public VmUpdateHostOptionsParamsData getData() {
return data;
}


public void setData(VmUpdateHostOptionsParamsData data) {
this.data = data;
}


public VmUpdateHostOptionsParams where(VmWhereInput where) {

this.where = where;
return this;
}

/**
/**
* Get where
*
* @return where
**/
**/
@javax.annotation.Nonnull
@ApiModelProperty(required = true, value = "")

public VmWhereInput getWhere() {
return where;
}


public void setWhere(VmWhereInput where) {
this.where = where;
}


@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down Expand Up @@ -113,6 +112,4 @@ private String toIndentedString(Object o) {
}
return o.toString().replace("\n", "\n ");
}

}

Loading