Как сериализовать специальный JSON с Gson (содержимое объекта вместо объекта) - PullRequest
0 голосов
/ 30 мая 2018

Я использую Java + Gson и хочу сериализовать специальный json.Я хочу содержимое объекта вместо объекта.

{
    "age": 26,
    "email": "norman@futurestud.io",
    "isDeveloper": true,
    "name": "Norman",

    "userAddress": {
        "city": "Magdeburg",
        "country": "Germany",
        "houseNumber": "42A",
        "street": "Main Street"
    }
}

возможно ли сериализовать в:

{
    "age": 26,
    "email": "norman@futurestud.io",
    "isDeveloper": true,
    "name": "Norman",
    "city": "Magdeburg",
    "country": "Germany",
    "houseNumber": "42A",
    "street": "Main Street"
}

Я пытаюсь использовать этот код, но неправильно:

@Expose
private final String age;
...
@Expose
@JsonAdapter(UserAddressSerializer.class)
private final UserAddress userAddress;
...


public class UserAddressSerializer implements JsonSerializer<UserAddress > {

    @Override
    public JsonElement serialize(UserAddress src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject result = new JsonObject();
        result.add("name", new JsonPrimitive(src.getName()));
        result.add("city", new JsonPrimitive(src.getCity()));
        result.add("country", new JsonPrimitive(src.getCountry()));
        result.add("houseNumber", new JsonPrimitive(src.getHouseNumber()));
        result.add("street", new JsonPrimitive(src.getStreet()));
        return result;
    }

}

Ответы [ 2 ]

0 голосов
/ 01 июня 2018

Я нахожу решение с использованием инструментов Gson (класс UserAddressAdapter extends TypeAdapter<UserAddress>).

public class User {

    ...

    // see the first ligne of UserAddressAdapter#write
    // city => (out.value(value.getCity());)
    @SerializedName("city")
    @JsonAdapter(UserAddressAdapter.class)
    private final UserAddress userAddress;
    ...

}


public class UserAddressAdapter extends TypeAdapter<UserAddress> {

    @Override
    public void write(JsonWriter out, UserAddress value) throws IOException {
        out.value(value.getCity());
        out.name("country");
        out.value(value.getCountry());
        out.name("houseNumber");
        out.value(value.getHouseNumber());
        out.name("street");
        out.value(value.getStreet());
    }

    @Override
    public UserAddress read(JsonReader in) throws IOException {
        final int city = in.nextInt();
        UserAddress[] scopes = UserAddress.values();
        for (UserAddress scope : scopes) {
            if (scope.getCity() == city) {
                return scope;
            }
        }
        return null;
    }

}

и вызывающий абонент:

new Gson().toJson(new User("...", ...))
0 голосов
/ 30 мая 2018

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классы, чтобы покрыть все случаи, но это также требует немного большего использования памяти из-за использования деревьев здесь.

...