Страницы

Поиск по вопросам

Показаны сообщения с ярлыком gson. Показать все сообщения
Показаны сообщения с ярлыком gson. Показать все сообщения

суббота, 11 января 2020 г.

Сериализация в JSON как объект конкретного класса

#java #gson


Есть у меня объект такого класса

class A {
  List items;
}


Я хочу его сериализовать в JSON. Пишу

A obj;
........
gson.toJson(obj);


Проблема: в items могут хранится как объекты BaseClass, так и его наследников. И
Gson сериализует все поля этих объектов.

Вопрос: нельзя ли как-то указать Gson сериализовать только поля, относящиеся к BaseClass
и его предкам. А поля наследников не трогать?

P.S. Наследники участвуют в сериализации других классов
    


Ответы

Ответ 1



Определяем свои правила конвертации объектов Gson Gson позволяет разработчикам определять свои собственные правила для сериализации и десериализации объектов. Зарегистрировать их можно с помощью метода registerTypeAdapter(). Для класса A(): class A { List items; } Предположим, что BaseClass имеет одно поле: class BaseClass { String field; } тогда создадим класс CustomSerializer и имплиментируем там соответствующий интерфейс: public class CustomSerializer implements JsonSerializer { @Override public JsonElement serialize(A src, Type type, JsonSerializationContext context) { JsonArray items = new JsonArray(); for (BaseClass item : src.items) { if (item.getClass().getSimpleName().equals("BaseClass")) { JsonObject currentItem = new JsonObject(); currentItem.addProperty("field", item.field); items.add(currentItem); } } return items; } } Примечание: класс сериализатор будет содержать только объекты типа BaseClass, чтобы он содержал еще и его предков, нужно добавить дополнительное условие. После этого регистрируем наш кастомный сериализатор: GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(A.class, new CustomSerializer()); Gson gson = builder.create(); Теперь можно смело вызывать метод A obj; ........ gson.toJson(obj); Для дерериализации аналогично создаем класс CustomDeserializer и имплиментируем там соответствующий интерфейс. Сперва добавим конструкторы для классов A и BaseClass: class A { List items; A(ArrayList items) { this.items = items; } } class BaseClass { String field; BaseClass(String field) { this.field = field; } } Теперь создаем класс CustomDeserializer: public class CustomDeserializer implements JsonDeserializer { @Override public A deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonArray items = (JsonArray) json; A a = new A(new ArrayList<>()); for (JsonElement item : items) { JsonObject currentItem = new JsonObject(); BaseClass baseClass = new BaseClass(item.getAsString()); a.items.add(baseClass); } return a; } } и регистрируем его аналогичным образом: GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(A.class, new CustomDeserializer()); Gson gson = builder.create(); Внимание: Если используется одновременно и сериализатор, и десериализатор, тогда нужно создать один класс, в котором имплементировать оба интерфейса, т.к. последовательная регистрация разных классов методом registerTypeAdapter будет переписывать предыдущую регистрацию.

Ответ 2



Update Или еще более простой вариант. Реализуем сериализатор для любого List public class GenericListSerializer implements JsonSerializer { @Override public JsonElement serialize(List src, Type type, JsonSerializationContext context) { JsonArray res = new JsonArray(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)type; Type[] args = paramType.getActualTypeArguments(); type = args[0]; } for (Object item : src) { JsonObject currentItem = (JsonObject)context.serialize(item, type); res.add(currentItem); } return res; } } А теперь помечаем поля специальной аннотацией class A { @JsonAdapter(GenericListSerializer.class) List items; } Все. теперь при сериализации поля items будет вызываться сериализатор GenericListSerializer Old version Благодаря пинку @Drakonoved получился такой адаптер public class CustomSerializer implements JsonSerializer
{ @Override public JsonElement serialize(A src, Type type, JsonSerializationContext context) { // Сериализуем поля родителя без изменений JsonObject res = (JsonObject)context.serialize(src, src.getClass().getSuperclass()); // Получаем свои поля Field[] flds = src.getClass().getDeclaredFields(); for (Field fld : flds) { Type fldType = fld.getGenericType(); JsonElement jField; Object fieldVal; try { // Получаем значение поля fieldVal = fld.get(src); } catch (IllegalAccessException e) { throw new RuntimeException(e); } // Если тип поля List и его наследники и это generic-тип if (fld.getType().isAssignableFrom(List.class) && fldType instanceof ParameterizedType) { // Получаем конкретные типы, которые были указаны в объявлении List<> ParameterizedType paramType = (ParameterizedType)fldType; Type[] args = paramType.getActualTypeArguments(); // формируем массив JsonArray jsonArray = new JsonArray(); for (Object item : (List)fieldVal) { // сериализуем элемент списка, как тип, который был указан при объявлении JsonObject currentItem = (JsonObject)context.serialize(item, args[0]); jsonArray.add(currentItem); } jField = jsonArray; } else { // Если у нас не список, то вызываем умолчательный сериализатор jField = context.serialize(fieldVal); } res.add(fld.getName(), jField); } return res; } }

четверг, 9 января 2020 г.

Преобразование JSON в POJO начиная с определенного уровня вложенности

#java #json #gson #pojo


Есть система, отдающая по REST API здоровенный JSON. На нее я воздействовать не могу
ни коем образом.

Из этого огромного JSON-а мне нужна только часть по определенному ключу.

Допустим из этого:

{
 "a":{
      "b":[.....]
     },
 "c":{.....},
 "r":{
     "d":{
          "e":{
               "f":[....],
               "g":[....]
              }
         },
      "m":{......}
   .....
   "z":{....}
}


Мне нужно сделать POJO структуру начиная с r.d.e.

Для того же GSON'а придется создавать как минимум всю ветку, что совершенно излишне
(или я заблуждаюсь?)

Есть ли какие библиотеки, которые можно использовать совместно с okhttp3 и которые
позволяют организовать такое?
    


Ответы

Ответ 1



Можно вручную разбирать весь JSON пока не дойдёте до нужного узла и только его парсить в объект. Типа как-то так: JsonParser parser = new JsonParser(); JsonObject obj = parser.parse(HUGE_JSON_STRING).getAsJsonObject(); String stringToParseNoramlly = obj.get("r").get("d").get("e").getAsString(); EModel eModel = new Gson().fromJson(stringToParseNoramlly, EModel.class);

вторник, 31 декабря 2019 г.

Эстетичное форматирование json файла

#java #json #форматирование #gson


Я использую библиотеку Gson для сериализации в json. Делаю я примерно вот так:

new Gson().toJson(new MyObject());


Но при этом у меня получается одна единая строка, без отступов и переносов:

{"classes":[{"name":"warrior","skills":[]}]}


Каким образом можно форматировать эту строку в более эстетичный вид, при сериализации,
что бы потом можно было удобно менять значения? Та строка должна быть примерно такая:

{
    "classes":
    [
        {
            "name": "warrior",
            "skills": []
        }
    ]
}

    


Ответы

Ответ 1



Добавьте настройку в билдер, а не просто создавайте инстанс Gson через конструктор: Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(new MyObject());

пятница, 20 декабря 2019 г.

Ошибочная сериализация полей с Gson

#java #gson


У меня в обьекте есть поле:

@SerializedName("CommentPhotoes")
@Expose(serialize = false)
private ArrayList commentPhotos;


Мне это поле нужно распарсить когда я считываю этот объект из строки, но когда я
его обратно сериализую в строку, это поле не должно туда попасть.

Проблема заключается в том что при сериализации объекта - это поле тоже сериализуется.
При чем тоже самое касается и десериализации. 

В чем может быть проблема, может я неправильно использую эти аннотации?
    


Ответы

Ответ 1



По умолчанию наличие аннотации @Expose ни на что не влияет. Поэтому при создании объекта Gson с помощью GsonBuilder нужно явно указать, как обрабатывать эти аннотации. Возможны варианты: Помечать нужные поля этой аннотацией Помечать игнорируемые поля этой аннотацией Использовать модификатор transient вместо аннотаций Подробнее: Помечаем поля, которые хотим сериализовать/десериализовать аннотацией @Expose, при создании Gson объекта говорим, что поля без этой аннотации надо игнорировать: Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); Помечаем поля, которые хотим игнорировать и в сериализации и в десериализации аннотацией @Expose, при создании Gson объекта добавляем ExclusionStrategies: Gson gson = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(Exclude.class) != null; } @Override public boolean shouldSkipClass(Class c) { return false; } }) .create(); Чтобы исключить поля только в сериализации или только в десериализации нужно добавлять аннотацию @Expose с параметром serialize=false или deserialize=false: class Book { // игнорируется только при сериализации @Expose(serialize = false) String field1; // игнорируется только при десериализации @Expose(deserialize = false) String field2; } и при создании Gson объекта настроить правильное исключение полей с этими аннотациями: new GsonBuilder() .addSerializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { Expose expose = f.getAnnotation(Expose.class); return expose != null && !expose.serialize(); } @Override public boolean shouldSkipClass(Class c) { return false; } }) .addDeserializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { Expose expose = f.getAnnotation(Expose.class); return expose != null && !expose.deserialize(); } @Override public boolean shouldSkipClass(Class c) { return false; } }) .create(); Также чтобы исключить поле можно добавить к нему модификатор transient: class Book { transient String name; }

Ответ 2



Возможно вы не настроили экземпляр Gson. Попробуйте так: new GsonBuilder() .addSerializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes fieldAttributes) { final Expose expose = fieldAttributes.getAnnotation(Expose.class); return expose != null && !expose.serialize(); } @Override public boolean shouldSkipClass(Class aClass) { return false; } }) .addDeserializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes fieldAttributes) { final Expose expose = fieldAttributes.getAnnotation(Expose.class); return expose != null && !expose.deserialize(); } @Override public boolean shouldSkipClass(Class aClass) { return false; } }) .create();

понедельник, 16 декабря 2019 г.

Обобщенный JSON

#java #json #gson


Сервер присылает ответы в формате

{response: {.......}}


например

{response: {code: 0}}
{response: {type: "test"}}


Пытаюсь сделать парсинг при помощи библиотеки gson в таком виде

class Response {
    private T response;
    public T getResponse() {
        return response;
    }
}

class CodeInfo {
    private int code;
}

class TypeInfo {
    private String type;
}

 T parse(String str) {
  Response resp = gson.fromJson(str, Response.class);
  return resp.getResponse();
}

CodeInfo = parse("{response: {code: 0}}");


Внутренние классы довольно обширные

На конструкции Response.class компилятор меня посылает. Решается ли задача без
кучи наследников Response? В принципе, могу даже сменить парсер
    


Ответы

Ответ 1



Спасибо @diraria за направление на путь истинный. Итого получилось public T parse(String str, Class AClass) { Response resp = JSON.fromJson( str, TypeToken.getParameterized(Response.class, AClass).getType() ); return resp.getResponse(); } CodeInfo info = parse("{response: {code: 0}}", CodeInfo.class) Если писать так public T parse(String str) { Response resp = JSON.fromJson( str, new TypeToken>(){}.getType() ); return resp.getResponse(); } то строка парсится, но resp.getResponse() возвращает объект класса com.google.gson.internal.LinkedTreeMap

Ответ 2



public T parse(String str) возвращает LinkedTreeMap, потому что T существует буквально только в воображении компилятора, и поэтому во время исполнения Gson не имеет достаточно информации о типе (заметьте, я сказал "тип", а не "класс"), которым параметризируется Response. У public T parse(String str, Class AClass) тоже проблемы, потому что объекты java.lang.Class не содержат информации о параметризации (так, впрочем, реализованы обобщённые типы в Java). Вследствие чего Response.class просто не имеет смысла (Class предоставляет информацию только о загруженном классе или о прититивном типе!), и поэтому компилятор ожидает Response.class (хотя, я думаю, что в Java можно было бы ввести что-то типа Response.type, определявший объект ParameterizedType; если я не ошибаюсь, аналогичная возможность есть в Kotlin). ParameterizedType, используемый Gson-ом на всю катушку, позволяет сохранять информацию о параметризации типа. Т.е., скорее нужно иметь следующую реализацию: private static T parse(final String json, final java.lang.reflect.Type type) { final Response response = gson.fromJson(json, TypeToken.getParameterized(Response.class, type).getType()); return response.response; } В этом случае, появляется возможность передавать в метод тип со сколь угодно сложной параметеризацией -- достаточно реализации интерфейса. Я, правда, уже не помню, есть ли в Gson готовые методы, позволяющие конструировать информацию о типах, не используя свои реализации. Но сам по себе Type не имеет сильного контроля типов, что чревато неприятными ошибками во время исполнения (например, ParameterizedType.getActualTypeArguments, возвращающий примитивный тип или неоответствие между типом переменной-приёмником и типом, описуемым ParameterizedType). Во избежение неприятностей и для ужесточения контроля типов в Gson имеется TypeToken (имеет аналоги в Google Guava, Jackson и Spring Framework под другими именами). Его задача -- содержать полную информацию о типе, определяемой ещё на этапе компиляции, что позволяет использовать компилятор для контроля типов. Сам TypeToken является абстрактным классом, заставляя в субклассах определять параметризацию, которую можно прочитать во время выполнения. И, как по мне, это позволяет весьма элегантно обходить ограничения, связанные с затиранием информации об обобщённых типах в Java: private static final TypeToken> responseCodeInfoTypeToken = new TypeToken>() { }; ... final CodeInfo codeInfo = parse("{response: {code: 1230}}", responseCodeInfoTypeToken); assert codeInfo.code == 1230; ... private static T parse(final String json, final TypeToken> typeToken) { final Response response = gson.fromJson(json, typeToken.getType()); return response.response; } TypeToken можно закешировать в статическом поле, он иммутабелен.

среда, 11 декабря 2019 г.

Как указать Gson`у, чтобы он использовал именно мой сериализатор/десериализатор?

#java #json #gson


Написал собственный конвертер, зарегистрировал в GsonBuilder'е. Но отрабатывает только
сериализатор и только в одном "rest-методе". 

То есть, он работает, но работает сериализация и десериализация по умолчанию, а мой
конвертер работает только при сериализации одного метода, когда получаю список объектов,
в остальных случаях игнорируется. В логах ошибок нет, тихо и не правильно работает,
не знаю уже что делать. Сталкивался ли кто-нибудь с таким, что не так идет?

Наверно это лишнее, но всё же распишу подробнее.

Провайдер для gson'а.

Класс GsonJsonProvider.java

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class GsonJsonProvider implements MessageBodyReader, MessageBodyWriter{

//---
//инициализация gson и его настроек
private Gson getGson(){
    if (gson == null) {
        gson = new GsonBuilder()
                .registerTypeAdapter(UUID.class, new UUIDConverter())
                .registerTypeAdapter(LocalDateTime.class, new DateTimeConverter())
                .registerTypeAdapter(Note.class, new NoteConverter())
                .setPrettyPrinting()
                .serializeNulls()
                .setLenient()
                .create();

        System.err.println("Gson Initialization");
    }
    System.err.println("Gson work");
    return gson;
}
//---
//метод getGson вызывается в этих методах и больше нигде
public Object readFrom(Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
    InputStreamReader streamReader = new InputStreamReader(entityStream, UTF_8);
    try {
        Type jsonType;
        if (type.equals(genericType)) {
            jsonType = type;
        } else {
            jsonType = genericType;
        }
        return getGson().fromJson(streamReader, jsonType);
    } finally {
        streamReader.close();
    }
}

//----
public void writeTo(Object object, Class type, Type genericType, Annotation[]
annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream
entityStream) throws IOException, WebApplicationException {
    OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8);
    try {
        Type jsonType;
        if (type.equals(genericType)) {
            jsonType = type;
        } else {
            jsonType = genericType;
        }
        getGson().toJson(object, jsonType, writer);
    } finally {
        writer.close();
    }
}


Класс модель Note.java

@Entity
@Table(name = "notes")
@NamedQuery(name = "Note.getAll", query = "SELECT n FROM Note n ORDER BY n.id ASC")
public class Note extends Model{

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_post")
private int id;
@Type(type = "org.hibernate.type.PostgresUUIDType")
private UUID uuid;
private String title;
private String text;
private String password;
private LocalDateTime date;
private int status;
//---


Класс конвертер NoteConverter.java

@Override
public JsonElement serialize(Note note, Type type, JsonSerializationContext context) {

    JsonObject result = new JsonObject();
    result.add("uuid", context.serialize(note.getUuid()));
    result.addProperty("title", note.getTitle());
    result.addProperty("text", note.getText());
    result.addProperty("password", note.getPassword() != null ? note.getPassword()
: "null_password");
    result.add("date", context.serialize(note.getDate()));
    result.addProperty("status", note.getStatus());

    return result;
}


и

@Override
public Note deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext
context) throws JsonParseException {

    JsonObject jsonObject = jsonElement.getAsJsonObject();

    Note note = new Note();
    note.setUuid(context.deserialize(jsonObject.get("uuid"), UUID.class));
    note.setTitle(jsonObject.get("title").getAsString());
    note.setText(jsonObject.get("text").getAsString());
    note.setPassword(jsonObject.get("password").getAsString());
    note.setDate(context.deserialize(jsonObject.get("date"), LocalDateTime.class));
    note.setStatus(jsonObject.get("status").getAsInt());

    return note;
}


Класс NoteResource.java

//вот здесь мой сериализатор отрабатывает, 
//отдаёт список объектов, где поле date в миллисекундах.
//в логах светится "Gson work" тот, что в GsonJsonProvider.java 
@GET
@Produces("application/json; charset=UTF-8")
public Response getAll(){
    List noteList = dao.getAll("Note.getAll", noteClass);
    return Response.ok(noteList).build();
}

//а здесь, работает сериализация по умолчанию
//и в логах "Gson work" не выходит, то есть он вообще не при делах, почему???
@GET
@Path("/{id}")
@Produces("application/json; charset=UTF-8")
public Response getNote(@PathParam("id") int id){
    Note note = dao.getById(id, noteClass);
    return Response.ok(note).build();
}

//и здесь тоже самое, сам объект note приходит
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response addNote(Note note){
    StatusResponse response = new StatusResponse();
    if (note == null) { //эту проверку проходит
        System.err.println("Added, note null!");
        return Response.status(Response.Status.BAD_REQUEST).entity(response).build();
    }
    System.err.println("Added, note title: " + note.getTitle());
    System.err.println("Added, uuid: " + note.getUuid());
//а здесь uuid = null, так как не знает как десериализовывать тип UUID
//------


UPDATE

Добавил параметризацию в класс GsonJsonProvider.java

public class GsonJsonProvider implements MessageBodyReader, MessageBodyWriter {
//...


Метод readFrom

public T readFrom(Class type, Type genericType, Annotation[] annotations, MediaType
mediaType, MultivaluedMap httpHeaders, InputStream entityStream) throws
IOException, WebApplicationException {
    InputStreamReader streamReader = new InputStreamReader(entityStream, UTF_8);
    try {
        //с genericType так же не работает
        return getGson().fromJson(streamReader, type);
    } finally {
        streamReader.close();
    }
}


Метод writeTo

public void writeTo(T t, Class type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream)
throws IOException, WebApplicationException {
    OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8);
    try {
        //если указывать type вместо genericType, то не работает
        getGson().toJson(t, genericType, writer);
    } finally {
        writer.close();
    }
}

//Сериализация работает как надо, только
//если изменить сеттер в модели Note.java
//но тогда вылетает ошибка при десериализации
//java.lang.ClassCastException: java.util.UUID cannot be cast to java.lang.String
public void setUuid(String uuidString) {
    this.uuid = UUID.fromString(uuidString);
}

//а с таким(нормальным) сеттером
//и сер/десериализация не работает (без ошибок в логах)
public void setUuid(UUID uuid) {
    this.uuid = uuid;
}

    


Ответы

Ответ 1



У вас проблема в куске: if (type.equals(genericType)) { jsonType = type; } else { jsonType = genericType; } У вас на выходе тип всегда будет genericType, то есть адаптер никогда не вызовется, поскольку вы всегда вызываете Gson с дженерик типом.

пятница, 1 марта 2019 г.

Сериализация в JSON как объект конкретного класса

Есть у меня объект такого класса
class A { List items; }
Я хочу его сериализовать в JSON. Пишу
A obj; ........ gson.toJson(obj);
Проблема: в items могут хранится как объекты BaseClass, так и его наследников. И Gson сериализует все поля этих объектов.
Вопрос: нельзя ли как-то указать Gson сериализовать только поля, относящиеся к BaseClass и его предкам. А поля наследников не трогать?
P.S. Наследники участвуют в сериализации других классов


Ответ

Update Или еще более простой вариант.
Реализуем сериализатор для любого List
public class GenericListSerializer implements JsonSerializer { @Override public JsonElement serialize(List src, Type type, JsonSerializationContext context) { JsonArray res = new JsonArray(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)type; Type[] args = paramType.getActualTypeArguments(); type = args[0]; } for (Object item : src) { JsonObject currentItem = (JsonObject)context.serialize(item, type); res.add(currentItem); } return res; } }
А теперь помечаем поля специальной аннотацией
class A { @JsonAdapter(GenericListSerializer.class) List items; }
Все. теперь при сериализации поля items будет вызываться сериализатор GenericListSerializer

Old version
Благодаря пинку @Drakonoved получился такой адаптер
public class CustomSerializer implements JsonSerializer { @Override public JsonElement serialize(A src, Type type, JsonSerializationContext context) { // Сериализуем поля родителя без изменений JsonObject res = (JsonObject)context.serialize(src, src.getClass().getSuperclass()); // Получаем свои поля Field[] flds = src.getClass().getDeclaredFields(); for (Field fld : flds) { Type fldType = fld.getGenericType(); JsonElement jField; Object fieldVal; try { // Получаем значение поля fieldVal = fld.get(src); } catch (IllegalAccessException e) { throw new RuntimeException(e); } // Если тип поля List и его наследники и это generic-тип if (fld.getType().isAssignableFrom(List.class) && fldType instanceof ParameterizedType) { // Получаем конкретные типы, которые были указаны в объявлении List<> ParameterizedType paramType = (ParameterizedType)fldType; Type[] args = paramType.getActualTypeArguments(); // формируем массив JsonArray jsonArray = new JsonArray(); for (Object item : (List)fieldVal) { // сериализуем элемент списка, как тип, который был указан при объявлении JsonObject currentItem = (JsonObject)context.serialize(item, args[0]); jsonArray.add(currentItem); } jField = jsonArray; } else { // Если у нас не список, то вызываем умолчательный сериализатор jField = context.serialize(fieldVal); } res.add(fld.getName(), jField); } return res; } }

пятница, 21 декабря 2018 г.

Ошибочная сериализация полей с Gson

У меня в обьекте есть поле:
@SerializedName("CommentPhotoes") @Expose(serialize = false) private ArrayList commentPhotos;
Мне это поле нужно распарсить когда я считываю этот объект из строки, но когда я его обратно сериализую в строку, это поле не должно туда попасть.
Проблема заключается в том что при сериализации объекта - это поле тоже сериализуется. При чем тоже самое касается и десериализации.
В чем может быть проблема, может я неправильно использую эти аннотации?


Ответ

По умолчанию наличие аннотации @Expose ни на что не влияет. Поэтому при создании объекта Gson с помощью GsonBuilder нужно явно указать, как обрабатывать эти аннотации.
Возможны варианты:
Помечать нужные поля этой аннотацией Помечать игнорируемые поля этой аннотацией Использовать модификатор transient вместо аннотаций
Подробнее:
Помечаем поля, которые хотим сериализовать/десериализовать аннотацией @Expose, при создании Gson объекта говорим, что поля без этой аннотации надо игнорировать:
Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); Помечаем поля, которые хотим игнорировать и в сериализации и в десериализации аннотацией @Expose, при создании Gson объекта добавляем ExclusionStrategies
Gson gson = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { return f.getAnnotation(Exclude.class) != null; }
@Override public boolean shouldSkipClass(Class c) { return false; } }) .create();
Чтобы исключить поля только в сериализации или только в десериализации нужно добавлять аннотацию @Expose с параметром serialize=false или deserialize=false
class Book { // игнорируется только при сериализации @Expose(serialize = false) String field1; // игнорируется только при десериализации @Expose(deserialize = false) String field2; }
и при создании Gson объекта настроить правильное исключение полей с этими аннотациями:
new GsonBuilder() .addSerializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { Expose expose = f.getAnnotation(Expose.class); return expose != null && !expose.serialize(); }
@Override public boolean shouldSkipClass(Class c) { return false; } }) .addDeserializationExclusionStrategy(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { Expose expose = f.getAnnotation(Expose.class); return expose != null && !expose.deserialize(); }
@Override public boolean shouldSkipClass(Class c) { return false; } }) .create(); Также чтобы исключить поле можно добавить к нему модификатор transient
class Book { transient String name; }

четверг, 20 декабря 2018 г.

Эстетичное форматирование json файла

Я использую библиотеку Gson для сериализации в json. Делаю я примерно вот так:
new Gson().toJson(new MyObject());
Но при этом у меня получается одна единая строка, без отступов и переносов:
{"classes":[{"name":"warrior","skills":[]}]}
Каким образом можно форматировать эту строку в более эстетичный вид, при сериализации, что бы потом можно было удобно менять значения? Та строка должна быть примерно такая:
{ "classes": [ { "name": "warrior", "skills": [] } ] }


Ответ

Добавьте настройку в билдер, а не просто создавайте инстанс Gson через конструктор:
Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(new MyObject());

суббота, 27 октября 2018 г.

Обобщенный JSON

Сервер присылает ответы в формате
{response: {.......}}
например
{response: {code: 0}} {response: {type: "test"}}
Пытаюсь сделать парсинг при помощи библиотеки gson в таком виде
class Response { private T response; public T getResponse() { return response; } }
class CodeInfo { private int code; }
class TypeInfo { private String type; }
T parse(String str) { Response resp = gson.fromJson(str, Response.class); return resp.getResponse(); }
CodeInfo = parse("{response: {code: 0}}");
Внутренние классы довольно обширные
На конструкции Response.class компилятор меня посылает. Решается ли задача без кучи наследников Response? В принципе, могу даже сменить парсер


Ответ

Спасибо @diraria за направление на путь истинный. Итого получилось
public T parse(String str, Class AClass) { Response resp = JSON.fromJson( str, TypeToken.getParameterized(Response.class, AClass).getType() ); return resp.getResponse(); }
CodeInfo info = parse("{response: {code: 0}}", CodeInfo.class)
Если писать так
public T parse(String str) { Response resp = JSON.fromJson( str, new TypeToken>(){}.getType() ); return resp.getResponse(); }
то строка парсится, но resp.getResponse() возвращает объект класса com.google.gson.internal.LinkedTreeMap