BAEL-7335: Polymorphism with Gson (#16320)

This commit is contained in:
Graham Cox 2024-04-09 07:59:09 +01:00 committed by GitHub
parent eead7a451c
commit e4c43225b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 364 additions and 0 deletions

View File

@ -0,0 +1,5 @@
package com.baeldung.gson.polymorphic;
public interface Shape {
double getArea();
}

View File

@ -0,0 +1,27 @@
package com.baeldung.gson.polymorphic;
import com.google.gson.*;
import java.lang.reflect.Type;
public class ShapeTypeAdapter implements JsonSerializer<Shape>, JsonDeserializer<Shape> {
@Override
public JsonElement serialize(Shape shape, Type type, JsonSerializationContext context) {
JsonElement elem = new Gson().toJsonTree(shape);
elem.getAsJsonObject().addProperty("type", shape.getClass().getName());
return elem;
}
@Override
public Shape deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
String typeName = jsonObject.get("type").getAsString();
try {
Class<? extends Shape> cls = (Class<? extends Shape>) Class.forName(typeName);
return new Gson().fromJson(json, cls);
} catch (ClassNotFoundException e) {
throw new JsonParseException(e);
}
}
}

View File

@ -0,0 +1,117 @@
package com.baeldung.gson.polymorphic;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TypeAdapterUnitTest {
@Test
void testSerialize() {
List<Shape> shapes = Arrays.asList(
new Circle(4d),
new Square(5d)
);
GsonBuilder builder = new GsonBuilder();
builder.registerTypeHierarchyAdapter(Shape.class, new ShapeTypeAdapter());
Gson gson = builder.create();
String json = gson.toJson(shapes);
assertEquals("[" +
"{" +
"\"radius\":4.0," +
"\"area\":50.26548245743669," +
"\"type\":\"com.baeldung.gson.polymorphic.TypeAdapterUnitTest$Circle\"" +
"},{" +
"\"side\":5.0," +
"\"area\":25.0," +
"\"type\":\"com.baeldung.gson.polymorphic.TypeAdapterUnitTest$Square\"" +
"}]", json);
}
@Test
void testDeserializeWrapper() {
List<Shape> shapes = Arrays.asList(
new Circle(4d),
new Square(5d)
);
GsonBuilder builder = new GsonBuilder();
builder.registerTypeHierarchyAdapter(Shape.class, new ShapeTypeAdapter());
Gson gson = builder.create();
String json = gson.toJson(shapes);
Type collectionType = new TypeToken<List<Shape>>(){}.getType();
List<Shape> result = gson.fromJson(json, collectionType);
assertEquals(shapes, result);
}
private static class Square implements Shape {
private final double side;
private final double area;
public Square(double side) {
this.side = side;
this.area = side * side;
}
@Override
public double getArea() {
return area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Square square = (Square) o;
return Double.compare(square.side, side) == 0 && Double.compare(square.area, area) == 0;
}
@Override
public int hashCode() {
return Objects.hash(side, area);
}
}
private static class Circle implements Shape {
private final double radius;
private final double area;
public Circle(double radius) {
this.radius = radius;
this.area = Math.PI * radius * radius;
}
@Override
public double getArea() {
return area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Circle circle = (Circle) o;
return Double.compare(circle.radius, radius) == 0 && Double.compare(circle.area, area) == 0;
}
@Override
public int hashCode() {
return Objects.hash(radius, area);
}
}
}

View File

@ -0,0 +1,67 @@
package com.baeldung.gson.polymorphic;
import com.google.gson.Gson;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TypeFieldUnitTest {
@Test
void testSerialize() {
List<Shape> shapes = Arrays.asList(
new Circle(4d),
new Square(5d)
);
Gson gson = new Gson();
String json = gson.toJson(shapes);
assertEquals("[" +
"{" +
"\"type\":\"circle\"," +
"\"radius\":4.0," +
"\"area\":50.26548245743669" +
"},{" +
"\"type\":\"square\"," +
"\"side\":5.0," +
"\"area\":25.0" +
"}]", json);
}
private static class Square implements Shape {
private final String type = "square";
private final double side;
private final double area;
public Square(double side) {
this.side = side;
this.area = side * side;
}
@Override
public double getArea() {
return area;
}
}
private static class Circle implements Shape {
private final String type = "circle";
private final double radius;
private final double area;
public Circle(double radius) {
this.radius = radius;
this.area = Math.PI * radius * radius;
}
@Override
public double getArea() {
return area;
}
}
}

View File

@ -0,0 +1,148 @@
package com.baeldung.gson.polymorphic;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class WrapperUnitTest {
@Test
void testSerializeWrapper() {
List<Wrapper> shapes = Arrays.asList(
new Wrapper(new Circle(4d)),
new Wrapper(new Square(5d))
);
Gson gson = new Gson();
String json = gson.toJson(shapes);
assertEquals("[" +
"{" +
"\"circle\":{" +
"\"radius\":4.0," +
"\"area\":50.26548245743669" +
"}" +
"},{" +
"\"square\":{" +
"\"side\":5.0," +
"\"area\":25.0" +
"}" +
"}]", json);
}
@Test
void testDeserializeWrapper() {
List<Wrapper> shapes = Arrays.asList(
new Wrapper(new Circle(4d)),
new Wrapper(new Square(5d))
);
Gson gson = new Gson();
String json = gson.toJson(shapes);
Type collectionType = new TypeToken<List<Wrapper>>(){}.getType();
List<Wrapper> result = gson.fromJson(json, collectionType);
assertEquals(shapes, result);
}
private static class Square implements Shape {
private final double side;
private final double area;
public Square(double side) {
this.side = side;
this.area = side * side;
}
@Override
public double getArea() {
return area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Square square = (Square) o;
return Double.compare(square.side, side) == 0 && Double.compare(square.area, area) == 0;
}
@Override
public int hashCode() {
return Objects.hash(side, area);
}
}
private static class Circle implements Shape {
private final double radius;
private final double area;
public Circle(double radius) {
this.radius = radius;
this.area = Math.PI * radius * radius;
}
@Override
public double getArea() {
return area;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Circle circle = (Circle) o;
return Double.compare(circle.radius, radius) == 0 && Double.compare(circle.area, area) == 0;
}
@Override
public int hashCode() {
return Objects.hash(radius, area);
}
}
private static class Wrapper {
private final Circle circle;
private final Square square;
public Wrapper(Circle circle) {
this.circle = circle;
this.square = null;
}
public Wrapper(Square square) {
this.square = square;
this.circle = null;
}
public Circle getCircle() {
return circle;
}
public Square getSquare() {
return square;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Wrapper wrapper = (Wrapper) o;
return Objects.equals(circle, wrapper.circle) && Objects.equals(square, wrapper.square);
}
@Override
public int hashCode() {
return Objects.hash(circle, square);
}
}
}