JSON деревья - это то, что вы ищете.Но, по крайней мере, одна оговорка - это разделение имен свойств с одинаковыми именами между различными объектами.
Предположим, у вас есть следующие классы данных:
final class User {
final String email;
final String name;
final int age;
final boolean isDeveloper;
User(final String email, final int age, final String name, final boolean isDeveloper) {
this.age = age;
this.email = email;
this.isDeveloper = isDeveloper;
this.name = name;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("email", email)
.add("name", name)
.add("age", age)
.add("isDeveloper", isDeveloper)
.toString();
}
}
final class UserAddress {
final String country;
final String city;
final String street;
final String houseNumber;
UserAddress(final String country, final String city, final String street, final String houseNumber) {
this.country = country;
this.city = city;
this.street = street;
this.houseNumber = houseNumber;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("country", country)
.add("city", city)
.add("street", street)
.add("houseNumber", houseNumber)
.toString();
}
}
Теперь вы можете использовать служебный методсериализовать несколько объектов в один экземпляр JsonObject
.Кроме того, вы можете легко иметь дубликат десериализации, который может десериализовать несколько объектов из экземпляра JsonObject
.Например:
final class JsonObjects {
private JsonObjects() {
}
@SafeVarargs
static JsonObject combine(final Gson gson, final Map.Entry<Object, ? extends Type>... objectAndTypeEntries) {
return Stream.of(objectAndTypeEntries)
// Each element should be serialized to a JsonElement
.map(e -> gson.toJsonTree(e.getKey(), e.getValue()))
// That must be a JSON object
.map(JsonElement::getAsJsonObject)
// And now we can collect a stream of JsonObject instances into a super-object
.collect(Collector.of(
JsonObject::new, // We'll collect into a new JsonObject instance, this is the accumulator
JsonObjects::mergeInto, // Merge each consecutive JsonObject instance into the accumulator
JsonObjects::mergeInto, // Or even combine multiple accumulators
Function.identity() // And just return the accumulator
));
}
static List<?> split(final Gson gson, final JsonObject jsonObject, final Type... types) {
return Stream.of(types)
// Each element should be deserialized from the JsonObject by a type
.map(type -> gson.fromJson(jsonObject, type))
// And just collect all the deserialized objects to a list
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
}
private static JsonObject mergeInto(final JsonObject to, final JsonObject from) {
for ( final Map.Entry<String, JsonElement> e : from.entrySet() ) {
to.add(e.getKey(), e.getValue());
}
return to;
}
}
Пример использования:
final User beforeUser = new User("norman@localhost", 26, "Norman", true);
System.out.println("BEFORE User: " + beforeUser);
final UserAddress beforeUserAddress = new UserAddress("Germany", "Magdeburg", "Main Street", "42A");
System.out.println("BEFORE User address: " + beforeUserAddress);
final JsonObject jsonObject = JsonObjects.combine(
gson,
new AbstractMap.SimpleImmutableEntry<>(beforeUser, User.class),
new AbstractMap.SimpleImmutableEntry<>(beforeUserAddress, UserAddress.class)
);
System.out.println("JSON: " + jsonObject);
final List<?> objects = JsonObjects.split(gson, jsonObject, User.class, UserAddress.class);
final User afterUser = (User) objects.get(0);
System.out.println("AFTER User: " + afterUser);
final UserAddress afterUserAddress = (UserAddress) objects.get(1);
System.out.println("AFTER User address: " + afterUserAddress);
Вывод:
BEFORE User: User{email=norman@localhost, name=Norman, age=26, isDeveloper=true}
BEFORE User address: UserAddress{country=Germany, city=Magdeburg, street=Main Street, houseNumber=42A}
JSON: {"email":"norman@localhost","name":"Norman","age":26,"isDeveloper":true,"country":"Germany","city":"Magdeburg","street":"Main Street","houseNumber":"42A"}
AFTER User: User{email=norman@localhost, name=Norman, age=26, isDeveloper=true}
AFTER User address: UserAddress{country=Germany, city=Magdeburg, street=Main Street, houseNumber=42A}
Как видите, вам не нужно создавать дополнительный DTOклассы, чтобы покрыть все случаи, но это также требует немного большего использования памяти из-за использования деревьев здесь.