Skip to content

Commit 667b378

Browse files
BenjaminKazemicopybara-github
authored andcommitted
chore: Migrate to Gson library.
PiperOrigin-RevId: 733406369
1 parent 2cdb435 commit 667b378

File tree

99 files changed

+2414
-1985
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+2414
-1985
lines changed

pom.xml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
<apache.httpcomponents.httpclient.version>4.5.13</apache.httpcomponents.httpclient.version>
4444
<apache.httpcomponents.httpcore.version>4.4.16</apache.httpcomponents.httpcore.version>
4545
<auto-value.version>1.11.0</auto-value.version>
46-
<jackson.version>2.17.2</jackson.version>
46+
<gson.version>2.12.1</gson.version>
47+
<auto-value-gson.version>1.3.1</auto-value-gson.version>
4748
<junit.version>5.11.4</junit.version>
4849
<mockito.version>3.12.4</mockito.version>
4950
</properties>
@@ -103,14 +104,14 @@
103104
<artifactId>api-common</artifactId>
104105
</dependency>
105106
<dependency>
106-
<groupId>com.fasterxml.jackson.core</groupId>
107-
<artifactId>jackson-databind</artifactId>
108-
<version>${jackson.version}</version>
107+
<groupId>com.google.code.gson</groupId>
108+
<artifactId>gson</artifactId>
109+
<version>${gson.version}</version>
109110
</dependency>
110111
<dependency>
111-
<groupId>com.fasterxml.jackson.datatype</groupId>
112-
<artifactId>jackson-datatype-jdk8</artifactId>
113-
<version>${jackson.version}</version>
112+
<groupId>com.ryanharter.auto.value</groupId>
113+
<artifactId>auto-value-gson</artifactId>
114+
<version>${auto-value-gson.version}</version>
114115
</dependency>
115116
<!-- JUnit 5 -->
116117
<dependency>
@@ -170,6 +171,11 @@
170171
<version>3.13.0</version>
171172
<configuration>
172173
<annotationProcessorPaths>
174+
<path>
175+
<groupId>com.ryanharter.auto.value</groupId>
176+
<artifactId>auto-value-gson</artifactId>
177+
<version>${auto-value-gson.version}</version>
178+
</path>
173179
<path>
174180
<groupId>com.google.auto.value</groupId>
175181
<artifactId>auto-value</artifactId>

src/main/java/com/google/genai/Common.java

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,57 +16,59 @@
1616

1717
package com.google.genai;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
20-
import com.fasterxml.jackson.databind.node.ArrayNode;
21-
import com.fasterxml.jackson.databind.node.ObjectNode;
22-
import java.util.Iterator;
19+
import com.google.gson.Gson;
20+
import com.google.gson.JsonArray;
21+
import com.google.gson.JsonElement;
22+
import com.google.gson.JsonObject;
23+
import com.google.gson.JsonPrimitive;
2324
import java.util.Map;
25+
import java.util.Set;
26+
import org.jspecify.annotations.Nullable;
2427

2528
/** Common utility methods for the GenAI SDK. */
2629
public final class Common {
2730

2831
private Common() {}
2932

30-
static void setValueByPath(ObjectNode jsonObject, String[] path, Object value) {
33+
static void setValueByPath(JsonObject jsonObject, String[] path, Object value) {
3134
if (path == null || path.length == 0) {
3235
throw new IllegalArgumentException("Path cannot be empty.");
3336
}
3437
if (jsonObject == null) {
3538
throw new IllegalArgumentException("JsonObject cannot be null.");
3639
}
3740

38-
ObjectNode currentObject = jsonObject;
41+
JsonObject currentObject = jsonObject;
3942
for (int i = 0; i < path.length - 1; i++) {
4043
String key = path[i];
4144
if (key.endsWith("[0]")) {
4245
String keyName = key.substring(0, key.length() - 3);
43-
ArrayNode arrayNode;
46+
JsonArray arrayNode;
4447
if (!currentObject.has(keyName)) {
45-
currentObject.putArray(keyName);
46-
arrayNode = (ArrayNode) currentObject.get(keyName);
47-
arrayNode.add(JsonSerializable.objectMapper.createObjectNode());
48+
arrayNode = new JsonArray();
49+
arrayNode.add(new JsonObject());
50+
currentObject.add(keyName, arrayNode);
4851
}
49-
arrayNode = (ArrayNode) currentObject.get(keyName);
50-
currentObject = (ObjectNode) arrayNode.get(0);
52+
arrayNode = (JsonArray) currentObject.get(keyName);
53+
currentObject = arrayNode.get(0).getAsJsonObject();
5154
} else {
5255
if (!currentObject.has(key)) {
53-
currentObject.putObject(key);
56+
currentObject.add(key, new JsonObject());
5457
}
55-
currentObject = (ObjectNode) currentObject.get(key);
58+
currentObject = currentObject.getAsJsonObject(key);
5659
}
5760
}
5861

59-
currentObject.put(path[path.length - 1], JsonSerializable.toJsonNode(value));
62+
currentObject.add(path[path.length - 1], toJsonElement(value));
6063
}
6164

62-
static String formatMap(String template, JsonNode data) {
63-
Iterator<Map.Entry<String, JsonNode>> fields = data.fields();
64-
while (fields.hasNext()) {
65-
Map.Entry<String, JsonNode> field = fields.next();
66-
String key = field.getKey();
65+
static String formatMap(String template, JsonObject data) {
66+
Set<Map.Entry<String, JsonElement>> entries = data.entrySet();
67+
for (Map.Entry<String, JsonElement> entry : entries) {
68+
String key = entry.getKey();
6769
String placeholder = "{" + key + "}";
6870
if (template.contains(placeholder)) {
69-
template = template.replace(placeholder, data.get(key).asText());
71+
template = template.replace(placeholder, entry.getValue().getAsString());
7072
}
7173
}
7274
return template;
@@ -91,23 +93,23 @@ static boolean isZero(Object obj) {
9193
return false;
9294
}
9395

94-
static Object getValueByPath(JsonNode object, String[] keys) {
96+
static JsonElement getValueByPath(JsonElement object, String[] keys) {
9597
if (object == null || keys == null) {
9698
return null;
9799
}
98100
if (keys.length == 1 && keys[0].equals("_self")) {
99101
return object;
100102
}
101103

102-
JsonNode currentObject = object;
104+
JsonElement currentElement = object;
103105

104106
for (String key : keys) {
105-
if (currentObject instanceof ObjectNode) {
106-
currentObject = currentObject.get(key);
107-
} else if (currentObject instanceof ArrayNode) {
107+
if (currentElement.isJsonObject()) {
108+
currentElement = currentElement.getAsJsonObject().get(key);
109+
} else if (currentElement.isJsonArray()) {
108110
try {
109111
int index = Integer.parseInt(key);
110-
currentObject = currentObject.get(index);
112+
currentElement = currentElement.getAsJsonArray().get(index);
111113
} catch (NumberFormatException | IndexOutOfBoundsException e) {
112114
return null;
113115
}
@@ -116,6 +118,27 @@ static Object getValueByPath(JsonNode object, String[] keys) {
116118
}
117119
}
118120

119-
return currentObject;
121+
return currentElement;
122+
}
123+
124+
private static @Nullable JsonElement toJsonElement(Object value) {
125+
if (value == null) {
126+
return null;
127+
} else if (value instanceof Number) {
128+
return new JsonPrimitive((Number) value);
129+
} else if (value instanceof String) {
130+
return new JsonPrimitive((String) value);
131+
} else if (value instanceof Boolean) {
132+
return new JsonPrimitive((Boolean) value);
133+
} else if (value instanceof Character) {
134+
return new JsonPrimitive(String.valueOf(value));
135+
} else if (value instanceof JsonObject) {
136+
return (JsonObject) value;
137+
} else if (value instanceof JsonArray) {
138+
return (JsonArray) value;
139+
} else {
140+
Gson gson = new Gson();
141+
return gson.toJsonTree(value);
142+
}
120143
}
121144
}

src/main/java/com/google/genai/HttpApiResponse.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package com.google.genai;
1818

19-
import com.fasterxml.jackson.databind.JsonNode;
19+
import com.google.gson.JsonObject;
2020
import java.io.IOException;
2121
import org.apache.http.HttpEntity;
2222
import org.apache.http.HttpException;
@@ -47,11 +47,13 @@ private String getErrorMessage() {
4747
return "";
4848
}
4949

50-
JsonNode errorNode = JsonSerializable.objectMapper.readTree(responseBody).get("error");
51-
if (errorNode != null && errorNode.isObject()) {
52-
JsonNode messageNode = errorNode.get("message");
53-
if (messageNode != null && messageNode.isTextual()) {
54-
return messageNode.asText();
50+
JsonObject jsonObject = JsonSerializable.gson.fromJson(responseBody, JsonObject.class);
51+
if (jsonObject != null && jsonObject.has("error") && jsonObject.get("error").isJsonObject()) {
52+
JsonObject errorObject = jsonObject.getAsJsonObject("error");
53+
if (errorObject.has("message")
54+
&& errorObject.get("message").isJsonPrimitive()
55+
&& errorObject.get("message").getAsJsonPrimitive().isString()) {
56+
return errorObject.get("message").getAsString();
5557
}
5658
}
5759
return "";
@@ -65,6 +67,7 @@ private String getErrorMessage() {
6567
*
6668
* @throws HttpException if the HTTP status code is not 200 OK.
6769
*/
70+
@Override
6871
public HttpEntity getEntity() throws HttpException {
6972
StatusLine statusLine = response.getStatusLine();
7073
if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
@@ -78,6 +81,7 @@ public HttpEntity getEntity() throws HttpException {
7881
}
7982

8083
/** Closes the Http response. */
84+
@Override
8185
public void close() throws IOException {
8286
this.response.close();
8387
}

src/main/java/com/google/genai/JsonSerializable.java

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,117 @@
1616

1717
package com.google.genai;
1818

19-
import com.fasterxml.jackson.annotation.JsonInclude;
20-
import com.fasterxml.jackson.core.JsonProcessingException;
21-
import com.fasterxml.jackson.databind.DeserializationFeature;
22-
import com.fasterxml.jackson.databind.JsonNode;
23-
import com.fasterxml.jackson.databind.ObjectMapper;
24-
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
19+
import com.google.gson.ExclusionStrategy;
20+
import com.google.gson.FieldAttributes;
21+
import com.google.gson.Gson;
22+
import com.google.gson.GsonBuilder;
23+
import com.google.gson.JsonDeserializationContext;
24+
import com.google.gson.JsonDeserializer;
25+
import com.google.gson.JsonElement;
26+
import com.google.gson.JsonParseException;
27+
import com.google.gson.JsonSerializationContext;
28+
import com.google.gson.JsonSerializer;
29+
import com.ryanharter.auto.value.gson.GenerateTypeAdapter;
30+
import java.lang.reflect.ParameterizedType;
31+
import java.lang.reflect.Type;
32+
import java.util.Optional;
33+
import org.jspecify.annotations.Nullable;
2534

2635
/** A class that can be serialized to JSON and deserialized from JSON. */
2736
public abstract class JsonSerializable {
2837

29-
protected static final ObjectMapper objectMapper = new ObjectMapper();
38+
protected static final Gson gson;
3039

3140
static {
32-
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_ABSENT);
33-
objectMapper.registerModule(new Jdk8Module());
34-
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
41+
gson =
42+
new GsonBuilder()
43+
.registerTypeAdapterFactory(GenerateTypeAdapter.FACTORY)
44+
.registerTypeAdapter(
45+
Optional.class,
46+
new JsonSerializer<Optional<?>>() {
47+
@Override
48+
public @Nullable JsonElement serialize(
49+
Optional<?> src, Type typeOfSrc, JsonSerializationContext context) {
50+
return src.isPresent() ? context.serialize(src.get()) : null;
51+
}
52+
})
53+
.registerTypeAdapter(
54+
Optional.class,
55+
new JsonDeserializer<Optional<?>>() {
56+
@Override
57+
public Optional<?> deserialize(
58+
JsonElement json, Type typeOfT, JsonDeserializationContext context)
59+
throws JsonParseException {
60+
if (json.isJsonNull()) {
61+
return Optional.empty();
62+
} else {
63+
Type actualType = null;
64+
if (typeOfT instanceof ParameterizedType) {
65+
actualType = ((ParameterizedType) typeOfT).getActualTypeArguments()[0];
66+
}
67+
return Optional.of(context.deserialize(json, actualType));
68+
}
69+
}
70+
})
71+
.setExclusionStrategies(
72+
new ExclusionStrategy() {
73+
@Override
74+
public boolean shouldSkipField(FieldAttributes f) {
75+
// Skip fields with null values
76+
try {
77+
Object value = f.getDeclaringClass().getDeclaredField(f.getName()).get(f);
78+
return value == null; // If field is null, exclude it
79+
} catch (IllegalAccessException | NoSuchFieldException e) {
80+
return false;
81+
}
82+
}
83+
84+
@Override
85+
public boolean shouldSkipClass(Class<?> clazz) {
86+
return false; // Don't skip classes by default
87+
}
88+
})
89+
.create();
3590
}
3691

3792
/** Serializes an object to a Json string. */
3893
protected static String toJsonString(Object object) {
39-
try {
40-
return objectMapper.writeValueAsString(object);
41-
} catch (JsonProcessingException e) {
42-
throw new IllegalStateException(e);
43-
}
94+
return gson.toJson(object);
4495
}
4596

4697
/** Serializes the instance to a Json string. */
4798
public String toJson() {
4899
return toJsonString(this);
49100
}
50101

51-
/** Serializes an object to a JsonNode. */
52-
protected static JsonNode toJsonNode(Object object) {
53-
return objectMapper.valueToTree(object);
102+
/** Deserializes a Json string to an object of the given type. */
103+
public static <T extends JsonSerializable> T fromJsonString(String jsonString, Class<T> clazz) {
104+
return gson.fromJson(jsonString, clazz);
54105
}
55106

56107
/** Deserializes a Json string to an object of the given type. */
57-
public static <T extends JsonSerializable> T fromJsonString(String jsonString, Class<T> clazz) {
58-
try {
59-
return objectMapper.readValue(jsonString, clazz);
60-
} catch (JsonProcessingException e) {
61-
throw new IllegalStateException(e);
62-
}
108+
public static Object fromJsonString(String jsonString, Type typeOfT) {
109+
return gson.fromJson(jsonString, typeOfT);
110+
}
111+
112+
/** Deserializes a Json string to an object of the given type. */
113+
public static JsonElement fromJsonString(String jsonString) {
114+
return gson.fromJson(jsonString, JsonElement.class);
115+
}
116+
117+
/** Deserializes a JsonElement to an object of the given type. */
118+
public static <T extends JsonSerializable> T fromJsonElement(
119+
JsonElement jsonElement, Class<T> clazz) {
120+
return gson.fromJson(jsonElement, clazz);
121+
}
122+
123+
/** Serializes the instance to a Json string. */
124+
public JsonElement toJsonElement() {
125+
return gson.toJsonTree(this);
63126
}
64127

65-
/** Deserializes a JsonNode to an object of the given type. */
66-
public static <T extends JsonSerializable> T fromJsonNode(JsonNode jsonNode, Class<T> clazz) {
67-
try {
68-
return objectMapper.treeToValue(jsonNode, clazz);
69-
} catch (JsonProcessingException e) {
70-
throw new IllegalStateException(e);
71-
}
128+
/** Serializes an object to a JsonElement. */
129+
protected static JsonElement toJsonElement(Object object) {
130+
return gson.toJsonTree(object);
72131
}
73132
}

0 commit comments

Comments
 (0)