Gson.toJson возвращает {} - PullRequest
       7

Gson.toJson возвращает {}

0 голосов
/ 06 ноября 2019

пользователей Отчет об ошибке показывает Gson().toJson(obj) иногда возвращает {}, но для большинства пользователей это работает правильно.

Я посетил пользователя, который столкнулся с ошибкой и отладкой приложения на его телефоне, и я сделал Toastчтобы показать, что отправляет на сервер, и я увидел тост показывает {}, а также Записи и ID не null.

вот что у меня естьготово.

private class ClassA{
        String ID;
        ArrayList<ClassB> Records;

        ClassA(String ID, ArrayList<ClassB> Records) {
            this.ID= ID;
            this.Records= Records;
        }
 }

 private class ClassB {
        int T;
        int F;
        String D;

        ClassB (int T, int F, String D) {
            this.T= T;
            this.F = F;
            this.D= D;
        }

}

И здесь я делаю сериализацию объекта

ClassA obj = new ClassA(ID,Records); 
String json = new Gson().toJson(obj);

, но new Gson().toJson(obj) для некоторых пользователей работает корректно, но для некоторых возвращается {}

Сервербаза данных показывает, что некоторые пользователи отправили данные {}, но некоторые правильные данные в формате json. После некоторых исследований, которые я обнаружил new Gson().toJson(obj), возвращается {} .no проблема веб-сервиса и никаких проблем с базой данных.

Дополнительная информация

ArrayList<ClassB> Records= new ArrayList<>();
Records.add(new ClassB(Integer.valueOf(t.toString()), Integer.valueOf(f.toString()),d.toString()));
ClassA obj = new ClassA(ID,Records); 
String json = new Gson().toJson(obj);

База данных

id    | data
----------------
c89   | {"ID":"c89","Records":[{"T":0,"F":0,"D":"2019/04/11 05:48 PM"}]} correct one

c90   | {} bug

Тест пустого ввода

Я выполнил тест ниже и обнаружил, что проблема выходит за рамкинулевые входы

ArrayList<ClassB> Records= new ArrayList<>();
ClassA obj = new ClassA(null,Records);    
Toast.makeText(getApplicationContext(),new Gson().toJson(obj ),Toast.LENGTH_LONG).show();

Тост показывает {"Records":[]}. хуже, чем верхнее условие, никогда не происходит

А также IDE говорит

if(ID!=null && Records!=null) <= this condition is always true 
ClassA obj = new ClassA(ID,Records); 

Proguard

##---------------Begin: proguard configuration for Gson  ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# For using GSON @Expose annotation
-keepattributes *Annotation*

# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.examples.android.model.** { <fields>; }

# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
  @com.google.gson.annotations.SerializedName <fields>;
}

##---------------End: proguard configuration for Gson  ----------

Как мне сделать так, чтобы он работал корректно для всех пользователей?

Комментарии

Это не должно быть возможно, вам нужно больше подробностей по случаю {}, например, является ли он последовательным, из-за многопоточности, это конкретная версия JDK

Нет никаких многопоточных явлений. Например, нет Runnable нет AsycTask нет Thread. Это просто обычный фрагмент, который получает данные от контент-провайдера и создает строку json.

Предлагаемое решение тот же результат!

ClassA obj = new ClassA(ID,Records); 
Gson gson = new GsonBuilder().serializeNulls().create();
Toast.makeText(getActivity(), gson.toJson(obj), Toast.LENGTH_LONG).show();

Тосты {}

Ответы [ 7 ]

3 голосов
/ 06 ноября 2019

Это потому, что ID и Records в ClassA равны нулю. Gson по умолчанию не сериализует пустые значения и на самом деле имеет метод, позволяющий включить сериализацию пустых значений, который я почти всегда включаю:

private static final Gson GSON = new GsonBuilder().serializeNulls().create();

Я рекомендую хранить статический экземпляр Gson, чтобы вам не приходилось хранитьсоздавая его каждый раз, когда вы хотите сериализовать.

Если вы не хотите сериализовать пустые значения, тогда у вас есть некоторые другие варианты для исправления кода, такие как проверка нулей в конструкторе ClassA.

1 голос
/ 14 ноября 2019

Просто мое дикое предположение, я видел, как вы упомянули в одном из комментариев, что ClassA и ClassB являются внутренними классами внутри фрагмента. Итак, может быть, есть несколько вещей, которые вы могли бы попробовать, если еще не сделали.

Первый: Как насчет их преобразования в статический внутренний класс?

private static class ClassA {
    ...
}

private static class ClassB {
    ...
}

Имеяклассы объявлены как внутренние классы, потому что никто другой не использует их, это нормально, я тоже иногда так делал. Но нестатический тип внутреннего класса зависит от экземпляра его родительского объекта, поэтому объявлять их статическим внутренним классом гораздо безопаснее, когда это просто простые классы bean / DTO.

Second: TryВозиться с Proguard Конфигурация с этими классами

Третий: Возможно, попробуйте извлечь эти классы внормальный класс, чтобы сузить область подозрений.

Если это все еще не работает, то проблема, вероятно, кроется где-то еще, возможно, в какой-то конкретной сборке / версии клиентского мобильного устройства или что-то в этом роде.

0 голосов
/ 14 ноября 2019

Возможно, вы где-нибудь ловите JsonParseException? RuntimeTypeAdapterFactory иногда вызывает проблемы, когда тип не зарегистрирован в TypeAdapter. Попробуйте назначить класс RuntimeTypeAdapterFactory и в той версии, которую использует ваш проект, заменить каждое вхождение

if (delegate == null) {
          throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
              + label + "; did you forget to register a subtype?");
        }

на

if (delegate == null) {
    /*
    This class is commandeered from Google/Gson. The edit below was added so that if a type
    is not registered with the TypeAdapter, the app still works instead of throwing an
    exception and crash.
    */
    delegate = (TypeAdapter<R>) labelToDelegate.get(Constants.DEFAULT_TYPE); // "DefaultType" 
}
0 голосов
/ 14 ноября 2019

Спасибо @Montri M за приятные предложения:

Второе: попробуйте поиграться с конфигурацией Proguard для этих классов

Вот вопрос о стеке потока о настройке Proguard для хранения внутренних классов.

Просмотрите класс -keep. ** {;} part

Третье: возможно, попробуйте извлечь эти классы в обычный класс, просто чтобы сузить подозрительную область.

я создал DataModel и извлек внутренние классы и вставил их в DataModel

public class DataModel {

    public static class ClassA{
        String ID;
        ArrayList<ClassB> Records;

        ClassA(String ID, ArrayList<ClassB> Records) {
            this.ID= ID;
            this.Records= Records;
        }
    }

    public static class ClassB {
        int T;
        int F;
        String D;

        ClassB (int T, int F, String D) {
            this.T= T;
            this.F = F;
            this.D= D;
        }

    }
}

И я поместил эти две строки в Proguard-rules.pro. Пожалуйста, обратите внимание на $ знак

-keep class <package-name>.DataModel.** { *; }
-keep class <package-name>.DataModel$** { *; }

Теперь я вижу, что проблема решена, и Gson сериализует obj правильно.

0 голосов
/ 13 ноября 2019

В вашем коде есть несколько интересных вещей, на которые следует обратить внимание:

  • Почему у вас есть частные уроки? Нужно ли это там?
  • Обычно я всегда использовал бы List (один из интерфейсов ArrayList) как тип для хранения списка в нем. ArrayList будет конкретным типом объекта, переданного туда.
  • Имена переменных всегда маленькие с регистром верблюда.

Для вашей проблемы: - Gson не конвертирует нулевые элементы по умолчанию. - Gson не преобразует переходные элементы по умолчанию.

Используйте GsonBuilder для настройки экземпляра Gson для вашего случая, для ваших нужд.

0 голосов
/ 08 ноября 2019

Попробуйте это

GsonBuilder builder = new GsonBuilder().serializeSpecialFloatingPointValues();
 String data = builder.create().toJson(obj);
0 голосов
/ 06 ноября 2019
ArrayList<ClassB> Records= new ArrayList<>();
ClassA obj = new ClassA(null,Records);    
Toast.makeText(getApplicationContext(),new Gson().toJson(obj 
),Toast.LENGTH_LONG).show();

В этом случае ваш rRecord arraylist является пустым, а не нулевым. попробуйте эту проверку

if(!Records.isEmpty()){ 
 // show toast
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...