Skip to content

Commit 41d7c77

Browse files
authored
Merge pull request #2054 from papegaaij/gson-type-adapters
feat: allows customization of JSON serialization through GSON TypeAdapters
1 parent 9decb26 commit 41d7c77

File tree

9 files changed

+511
-109
lines changed

9 files changed

+511
-109
lines changed

components/serialization/json/spotBugsExcludeFilter.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubu
1818
<Or>
1919
<Class name="com.microsoft.kiota.serialization.mocks.IntersectionTypeMock" />
2020
<Class name="com.microsoft.kiota.serialization.mocks.UnionTypeMock" />
21+
<Class name="com.microsoft.kiota.serialization.TestParsable" />
2122
</Or>
2223
</Match>
2324
<Match>
@@ -31,4 +32,4 @@ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubu
3132
<Bug pattern="NP_BOOLEAN_RETURN_NULL" />
3233
<Class name="com.microsoft.kiota.serialization.JsonParseNode" />
3334
</Match>
34-
</FindBugsFilter>
35+
</FindBugsFilter>
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.microsoft.kiota.serialization;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.GsonBuilder;
5+
import com.google.gson.JsonSyntaxException;
6+
import com.google.gson.TypeAdapter;
7+
import com.google.gson.stream.JsonReader;
8+
import com.google.gson.stream.JsonWriter;
9+
import com.microsoft.kiota.PeriodAndDuration;
10+
11+
import jakarta.annotation.Nonnull;
12+
13+
import java.io.IOException;
14+
import java.time.LocalDate;
15+
import java.time.LocalDateTime;
16+
import java.time.LocalTime;
17+
import java.time.OffsetDateTime;
18+
import java.time.ZoneOffset;
19+
import java.time.format.DateTimeFormatter;
20+
import java.time.format.DateTimeParseException;
21+
import java.util.Base64;
22+
23+
public class DefaultGsonBuilder {
24+
25+
private static final TypeAdapter<OffsetDateTime> OFFSET_DATE_TIME =
26+
new TypeAdapter<OffsetDateTime>() {
27+
@Override
28+
public OffsetDateTime read(JsonReader in) throws IOException {
29+
String stringValue = in.nextString();
30+
try {
31+
return OffsetDateTime.parse(stringValue);
32+
} catch (DateTimeParseException ex) {
33+
// Append UTC offset if it's missing
34+
try {
35+
LocalDateTime localDateTime = LocalDateTime.parse(stringValue);
36+
return localDateTime.atOffset(ZoneOffset.UTC);
37+
} catch (DateTimeParseException ex2) {
38+
throw new JsonSyntaxException(
39+
"Failed parsing '"
40+
+ stringValue
41+
+ "' as OffsetDateTime; at path "
42+
+ in.getPreviousPath(),
43+
ex2);
44+
}
45+
}
46+
}
47+
48+
@Override
49+
public void write(JsonWriter out, OffsetDateTime value) throws IOException {
50+
out.value(value.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
51+
}
52+
};
53+
54+
private static final TypeAdapter<LocalDate> LOCAL_DATE =
55+
new TypeAdapter<LocalDate>() {
56+
@Override
57+
public LocalDate read(JsonReader in) throws IOException {
58+
String stringValue = in.nextString();
59+
try {
60+
return LocalDate.parse(stringValue);
61+
} catch (DateTimeParseException ex) {
62+
throw new JsonSyntaxException(
63+
"Failed parsing '"
64+
+ stringValue
65+
+ "' as LocalDate; at path "
66+
+ in.getPreviousPath(),
67+
ex);
68+
}
69+
}
70+
71+
@Override
72+
public void write(JsonWriter out, LocalDate value) throws IOException {
73+
out.value(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
74+
}
75+
};
76+
77+
private static final TypeAdapter<LocalTime> LOCAL_TIME =
78+
new TypeAdapter<LocalTime>() {
79+
@Override
80+
public LocalTime read(JsonReader in) throws IOException {
81+
String stringValue = in.nextString();
82+
try {
83+
return LocalTime.parse(stringValue);
84+
} catch (DateTimeParseException ex) {
85+
throw new JsonSyntaxException(
86+
"Failed parsing '"
87+
+ stringValue
88+
+ "' as LocalTime; at path "
89+
+ in.getPreviousPath(),
90+
ex);
91+
}
92+
}
93+
94+
@Override
95+
public void write(JsonWriter out, LocalTime value) throws IOException {
96+
out.value(value.format(DateTimeFormatter.ISO_LOCAL_TIME));
97+
}
98+
};
99+
100+
private static final TypeAdapter<PeriodAndDuration> PERIOD_AND_DURATION =
101+
new TypeAdapter<PeriodAndDuration>() {
102+
@Override
103+
public PeriodAndDuration read(JsonReader in) throws IOException {
104+
String stringValue = in.nextString();
105+
try {
106+
return PeriodAndDuration.parse(stringValue);
107+
} catch (DateTimeParseException ex) {
108+
throw new JsonSyntaxException(
109+
"Failed parsing '"
110+
+ stringValue
111+
+ "' as PeriodAndDuration; at path "
112+
+ in.getPreviousPath(),
113+
ex);
114+
}
115+
}
116+
117+
@Override
118+
public void write(JsonWriter out, PeriodAndDuration value) throws IOException {
119+
out.value(value.toString());
120+
}
121+
};
122+
123+
private static final TypeAdapter<byte[]> BYTE_ARRAY =
124+
new TypeAdapter<byte[]>() {
125+
@Override
126+
public byte[] read(JsonReader in) throws IOException {
127+
String stringValue = in.nextString();
128+
try {
129+
if (stringValue.isEmpty()) {
130+
return null;
131+
}
132+
return Base64.getDecoder().decode(stringValue);
133+
} catch (IllegalArgumentException ex) {
134+
throw new JsonSyntaxException(
135+
"Failed parsing '"
136+
+ stringValue
137+
+ "' as byte[]; at path "
138+
+ in.getPreviousPath(),
139+
ex);
140+
}
141+
}
142+
143+
@Override
144+
public void write(JsonWriter out, byte[] value) throws IOException {
145+
out.value(Base64.getEncoder().encodeToString(value));
146+
}
147+
};
148+
149+
private static final Gson defaultInstance = getDefaultBuilder().create();
150+
151+
public static @Nonnull Gson getDefaultInstance() {
152+
return defaultInstance;
153+
}
154+
155+
public static @Nonnull GsonBuilder getDefaultBuilder() {
156+
return new GsonBuilder()
157+
.registerTypeAdapter(OffsetDateTime.class, OFFSET_DATE_TIME.nullSafe())
158+
.registerTypeAdapter(LocalDate.class, LOCAL_DATE.nullSafe())
159+
.registerTypeAdapter(LocalTime.class, LOCAL_TIME.nullSafe())
160+
.registerTypeAdapter(PeriodAndDuration.class, PERIOD_AND_DURATION.nullSafe())
161+
.registerTypeAdapter(byte[].class, BYTE_ARRAY.nullSafe());
162+
}
163+
}

0 commit comments

Comments
 (0)