Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.microsoft.kiota.serialization;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.microsoft.kiota.PeriodAndDuration;

import java.io.IOException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.util.Base64;

public class DefaultGsonBuilder {

private static final TypeAdapter<OffsetDateTime> OFFSET_DATE_TIME =
new TypeAdapter<>() {
@Override
public OffsetDateTime read(JsonReader in) throws IOException {
String stringValue = in.nextString();
try {
return OffsetDateTime.parse(stringValue);
} catch (DateTimeParseException ex) {
// Append UTC offset if it's missing
try {
LocalDateTime localDateTime = LocalDateTime.parse(stringValue);
return localDateTime.atOffset(ZoneOffset.UTC);
} catch (DateTimeParseException ex2) {
throw new JsonSyntaxException(
"Failed parsing '"
+ stringValue
+ "' as OffsetDateTime; at path "
+ in.getPreviousPath(),
ex2);
}
}
}

@Override
public void write(JsonWriter out, OffsetDateTime value) throws IOException {
out.value(value.toString());
}
};

private static final TypeAdapter<LocalDate> LOCAL_DATE =
new TypeAdapter<>() {
@Override
public LocalDate read(JsonReader in) throws IOException {
String stringValue = in.nextString();
try {
return LocalDate.parse(stringValue);
} catch (DateTimeParseException ex) {
throw new JsonSyntaxException(
"Failed parsing '"
+ stringValue
+ "' as LocalDate; at path "
+ in.getPreviousPath(),
ex);
}
}

@Override
public void write(JsonWriter out, LocalDate value) throws IOException {
out.value(value.toString());
}
};

private static final TypeAdapter<LocalTime> LOCAL_TIME =
new TypeAdapter<>() {
@Override
public LocalTime read(JsonReader in) throws IOException {
String stringValue = in.nextString();
try {
return LocalTime.parse(stringValue);
} catch (DateTimeParseException ex) {
throw new JsonSyntaxException(
"Failed parsing '"
+ stringValue
+ "' as LocalTime; at path "
+ in.getPreviousPath(),
ex);
}
}

@Override
public void write(JsonWriter out, LocalTime value) throws IOException {
out.value(value.toString());
}
};

private static final TypeAdapter<PeriodAndDuration> PERIOD_AND_DURATION =
new TypeAdapter<>() {
@Override
public PeriodAndDuration read(JsonReader in) throws IOException {
String stringValue = in.nextString();
try {
return PeriodAndDuration.parse(stringValue);
} catch (DateTimeParseException ex) {
throw new JsonSyntaxException(
"Failed parsing '"
+ stringValue
+ "' as PeriodAndDuration; at path "
+ in.getPreviousPath(),
ex);
}
}

@Override
public void write(JsonWriter out, PeriodAndDuration value) throws IOException {
out.value(value.toString());
}
};

private static final TypeAdapter<byte[]> BYTE_ARRAY =
new TypeAdapter<>() {
@Override
public byte[] read(JsonReader in) throws IOException {
String stringValue = in.nextString();
try {
if (stringValue.isEmpty()) {
return null;
}
return Base64.getDecoder().decode(stringValue);
} catch (IllegalArgumentException ex) {
throw new JsonSyntaxException(
"Failed parsing '"
+ stringValue
+ "' as byte[]; at path "
+ in.getPreviousPath(),
ex);
}
}

@Override
public void write(JsonWriter out, byte[] value) throws IOException {
out.value(Base64.getEncoder().encodeToString(value));
}
};

private static final Gson defaultInstance = getDefaultBuilder().create();

public static Gson getDefaultInstance() {
return defaultInstance;
}

public static GsonBuilder getDefaultBuilder() {
return new GsonBuilder()
.registerTypeAdapter(OffsetDateTime.class, OFFSET_DATE_TIME.nullSafe())
.registerTypeAdapter(LocalDate.class, LOCAL_DATE.nullSafe())
.registerTypeAdapter(LocalTime.class, LOCAL_TIME.nullSafe())
.registerTypeAdapter(PeriodAndDuration.class, PERIOD_AND_DURATION.nullSafe())
.registerTypeAdapter(byte[].class, BYTE_ARRAY.nullSafe());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.microsoft.kiota.serialization;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
Expand All @@ -11,13 +12,9 @@

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
Expand All @@ -31,13 +28,33 @@
/** ParseNode implementation for JSON */
public class JsonParseNode implements ParseNode {
private final JsonElement currentNode;
private final Gson gson;

/**
* Creates a new instance of the JsonParseNode class.
* @param node the node to wrap.
*/
public JsonParseNode(@Nonnull final JsonElement node) {
this(node, DefaultGsonBuilder.getDefaultInstance());
}
Comment thread
papegaaij marked this conversation as resolved.

/**
* Creates a new instance of the JsonParseNode class.
* @param node the node to wrap.
* @param node the node to wrap.
Comment thread
baywet marked this conversation as resolved.
Outdated
*/
public JsonParseNode(@Nonnull final JsonElement node, @Nonnull final Gson gson) {
currentNode = Objects.requireNonNull(node, "parameter node cannot be null");
this.gson = Objects.requireNonNull(gson, "parameter gson cannot be null");
}

/**
* Creates a new {@link JsonParseNode} for the given {@link JsonElement}.
* @param node the node to wrap.
* @return the newly created {@link JsonParseNode}.
*/
@Nonnull private JsonParseNode createNewNode(@Nonnull JsonElement node) {
return new JsonParseNode(node, gson);
}

/** {@inheritDoc} */
Expand All @@ -47,122 +64,72 @@ public JsonParseNode(@Nonnull final JsonElement node) {
final JsonObject object = currentNode.getAsJsonObject();
final JsonElement childNodeElement = object.get(identifier);
if (childNodeElement == null) return null;
final JsonParseNode result = new JsonParseNode(childNodeElement);
final JsonParseNode result = createNewNode(childNodeElement);
result.setOnBeforeAssignFieldValues(this.onBeforeAssignFieldValues);
result.setOnAfterAssignFieldValues(this.onAfterAssignFieldValues);
return result;
} else return null;
}

@Nullable public String getStringValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsString() : null;
return gson.fromJson(currentNode, String.class);
Comment thread
papegaaij marked this conversation as resolved.
Outdated
}

@Nullable public Boolean getBooleanValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsBoolean() : null;
return gson.fromJson(currentNode, Boolean.class);
}

@Nullable public Byte getByteValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsByte() : null;
return gson.fromJson(currentNode, Byte.class);
}

@Nullable public Short getShortValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsShort() : null;
return gson.fromJson(currentNode, Short.class);
}

@Nullable public BigDecimal getBigDecimalValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsBigDecimal() : null;
return gson.fromJson(currentNode, BigDecimal.class);
}

@Nullable public Integer getIntegerValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsInt() : null;
return gson.fromJson(currentNode, Integer.class);
}

@Nullable public Float getFloatValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsFloat() : null;
return gson.fromJson(currentNode, Float.class);
}

@Nullable public Double getDoubleValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsDouble() : null;
return gson.fromJson(currentNode, Double.class);
}

@Nullable public Long getLongValue() {
return currentNode.isJsonPrimitive() ? currentNode.getAsLong() : null;
return gson.fromJson(currentNode, Long.class);
}

@Nullable public UUID getUUIDValue() {
final String stringValue = currentNode.getAsString();
if (stringValue == null) return null;
return UUID.fromString(stringValue);
return gson.fromJson(currentNode, UUID.class);
Comment thread
papegaaij marked this conversation as resolved.
}

@Nullable public OffsetDateTime getOffsetDateTimeValue() {
final String stringValue = currentNode.getAsString();
if (stringValue == null) return null;
try {
return OffsetDateTime.parse(stringValue);
} catch (DateTimeParseException ex) {
// Append UTC offset if it's missing
try {
LocalDateTime localDateTime = LocalDateTime.parse(stringValue);
return localDateTime.atOffset(ZoneOffset.UTC);
} catch (DateTimeParseException ex2) {
throw ex;
}
}
return gson.fromJson(currentNode, OffsetDateTime.class);
}

@Nullable public LocalDate getLocalDateValue() {
final String stringValue = currentNode.getAsString();
if (stringValue == null) return null;
return LocalDate.parse(stringValue);
return gson.fromJson(currentNode, LocalDate.class);
}

@Nullable public LocalTime getLocalTimeValue() {
final String stringValue = currentNode.getAsString();
if (stringValue == null) return null;
return LocalTime.parse(stringValue);
return gson.fromJson(currentNode, LocalTime.class);
}

@Nullable public PeriodAndDuration getPeriodAndDurationValue() {
final String stringValue = currentNode.getAsString();
if (stringValue == null) return null;
return PeriodAndDuration.parse(stringValue);
return gson.fromJson(currentNode, PeriodAndDuration.class);
}

@Nullable private <T> T getPrimitiveValue(
@Nonnull final Class<T> targetClass, @Nonnull final JsonParseNode itemNode) {
if (targetClass == Boolean.class) {
return (T) itemNode.getBooleanValue();
} else if (targetClass == Short.class) {
return (T) itemNode.getShortValue();
} else if (targetClass == Byte.class) {
return (T) itemNode.getByteValue();
} else if (targetClass == BigDecimal.class) {
return (T) itemNode.getBigDecimalValue();
} else if (targetClass == String.class) {
return (T) itemNode.getStringValue();
} else if (targetClass == Integer.class) {
return (T) itemNode.getIntegerValue();
} else if (targetClass == Float.class) {
return (T) itemNode.getFloatValue();
} else if (targetClass == Double.class) {
return (T) itemNode.getDoubleValue();
} else if (targetClass == Long.class) {
return (T) itemNode.getLongValue();
} else if (targetClass == UUID.class) {
return (T) itemNode.getUUIDValue();
} else if (targetClass == OffsetDateTime.class) {
return (T) itemNode.getOffsetDateTimeValue();
} else if (targetClass == LocalDate.class) {
return (T) itemNode.getLocalDateValue();
} else if (targetClass == LocalTime.class) {
return (T) itemNode.getLocalTimeValue();
} else if (targetClass == PeriodAndDuration.class) {
return (T) itemNode.getPeriodAndDurationValue();
} else {
throw new RuntimeException("unknown type to deserialize " + targetClass.getName());
}
return gson.fromJson(itemNode.currentNode, targetClass);
}

private <T> List<T> iterateOnArray(JsonElement jsonElement, Function<JsonParseNode, T> fn) {
Expand All @@ -171,7 +138,7 @@ private <T> List<T> iterateOnArray(JsonElement jsonElement, Function<JsonParseNo
final List<T> result = new ArrayList<>();
while (sourceIterator.hasNext()) {
final JsonElement item = sourceIterator.next();
final JsonParseNode itemNode = new JsonParseNode(item);
final JsonParseNode itemNode = createNewNode(item);
itemNode.setOnBeforeAssignFieldValues(this.getOnBeforeAssignFieldValues());
itemNode.setOnAfterAssignFieldValues(this.getOnAfterAssignFieldValues());
result.add(fn.apply(itemNode));
Expand Down Expand Up @@ -237,7 +204,7 @@ else if (element.isJsonPrimitive()) {
element.getAsJsonObject().entrySet()) {
final String fieldKey = fieldEntry.getKey();
final JsonElement fieldValue = fieldEntry.getValue();
final JsonParseNode childNode = new JsonParseNode(fieldValue);
final JsonParseNode childNode = createNewNode(fieldValue);
childNode.setOnBeforeAssignFieldValues(this.getOnBeforeAssignFieldValues());
childNode.setOnAfterAssignFieldValues(this.getOnAfterAssignFieldValues());
propertiesMap.put(fieldKey, childNode.getUntypedValue());
Expand Down Expand Up @@ -294,7 +261,7 @@ private <T extends Parsable> void assignFieldValues(
final JsonElement fieldValue = fieldEntry.getValue();
if (fieldValue.isJsonNull()) continue;
if (fieldDeserializer != null) {
final JsonParseNode itemNode = new JsonParseNode(fieldValue);
final JsonParseNode itemNode = createNewNode(fieldValue);
itemNode.setOnBeforeAssignFieldValues(this.onBeforeAssignFieldValues);
itemNode.setOnAfterAssignFieldValues(this.onAfterAssignFieldValues);
fieldDeserializer.accept(itemNode);
Expand Down Expand Up @@ -344,10 +311,6 @@ public void setOnAfterAssignFieldValues(@Nullable final Consumer<Parsable> value
}

@Nullable public byte[] getByteArrayValue() {
final String base64 = this.getStringValue();
if (base64 == null || base64.isEmpty()) {
return null;
}
return Base64.getDecoder().decode(base64);
return gson.fromJson(currentNode, byte[].class);
}
}
Loading