如何在模型中表示此结构?

时间:2017-05-24 17:41:40

标签: android json retrofit2

我使用Retrofit2获取博客新闻,每个新闻都有 n 练习,以学习具有以下代码结构的西班牙语:

练习响应部分

"exercises": {
      "number_of_exercises": 2,
      "exercise1": {
        "title": "Ejercicio de comprensión B.1 (Comprehension B.1)",
        "question1": "La noticia habla...",
        "answer1_1": "de un test para evaluar la inteligencia humana.",
        "answer1_2": "de un descubrimiento científico relacionado con el ser humano.",
        "answer1_3": "del descubrimiento de una nueva especie animal.",
        "correctAnswer1": "de un descubrimiento científico relacionado con el ser humano.",
        "question2": "Los genes identificados...",
        "answer2_1": "pueden explicar algunos comportamientos sociales del ser humano.",
        "answer2_2": "explican el funcionamiento completo del cerebro.",
        "answer2_3": "están relacionados con la inteligencia.",
        "correctAnswer2": "están relacionados con la inteligencia.",
        "question3": "Este descubrimiento...",
        "answer3_1": "no aportará nada a las investigaciones sobre el cerebro.",
        "answer3_2": "podría ofrecer nuevos datos sobre el funcionamiento del cerebro.",
        "answer3_3": "no es muy importante para la biología.",
        "correctAnswer3": "podría ofrecer nuevos datos sobre el funcionamiento del cerebro.",
        "question4": "Según el texto, los científicos ya saben todo sobre los procesos cognitivos.",
        "answer4_1": "No se sabe.",
        "answer4_2": "Verdadero.",
        "answer4_3": "Falso.",
        "correctAnswer4": "Falso."
      },
      "exercise2": {
        "title": "Ejercicio de vocabulario B.2 (Vocabulary B.2)",
        "question1": "Relacionas 'cognitivo' con...",
        "answer1_1": "la empatía.",
        "answer1_2": "el conocimiento.",
        "answer1_3": "una congestión.",
        "correctAnswer1": "el conocimiento.",
        "question2": "'Cohorte' es igual que...",
        "answer2_1": "'conjunto'.",
        "answer2_2": "'comprensión'.",
        "answer2_3": "'finalización'.",
        "correctAnswer2": "'conjunto'.",
        "question3": "Lo contrario de 'específico' es...",
        "answer3_1": "'genérico'.",
        "answer3_2": "'aleatorio'.",
        "answer3_3": "'concreto'.",
        "correctAnswer3": "'genérico'.",
        "question4": "El 'genoma' está relacionado con el ADN.",
        "answer4_1": "No se sabe.",
        "answer4_2": "Falso.",
        "answer4_3": "Verdadero.",
        "correctAnswer4": "Verdadero."
      }
    }

由于练习的数量不同(有时是1或更多),我不知道如何根据他们所拥有的结构类型在课堂上表示它们。

你能帮帮我吗?

我已经尝试创建一个类,但是使用子类Exercise1,Exercise2等...我正在寻找完成此要求的最佳方法

2 个答案:

答案 0 :(得分:0)

试试这个:

try {
    ArrayList<Exercise> exercises = new ArrayList<>();
    JSONObject json = new JSONObject(yourJson);
    int exerciseQuantity = json.getInt("number_of_exercises");
    for (int i = 1; i <= exerciseQuantity; i++) {
        Exercise e = json.get("exercise".concat(String.valueOf(i)));
        if (e != null) {
            exercises.add(e);
        }
    }
} catch (JSONException e) {
    e.printStackTrace();
}

答案 1 :(得分:0)

如果您正在使用Retrofit,那么您可能也在使用Gson。 至少,Gson允许实现精彩的东西,即使是设计糟糕的JSON文档。

首先,您可以设计合适的对象模型,以方便使用。

final class ExerciseWrapper {

    final List<Exercise> exercises;

    ExerciseWrapper(final List<Exercise> exercises) {
        this.exercises = exercises;
    }

}
final class Exercise {

    final String title;
    final List<Question> questions;

    Exercise(final String title, final List<Question> questions) {
        this.title = title;
        this.questions = questions;
    }

}
final class Question {

    final String question;
    final List<String> answers;
    final int correctAnswer;

    Question(final String question, final List<String> answers, final int correctAnswer) {
        this.question = question;
        this.answers = answers;
        this.correctAnswer = correctAnswer;
    }

}

现在,定义一些接口以实现反序列化器的各种策略。 这些都是微不足道的,你可以使用任何类似的东西(例如,如果你使用的是Google Guava,只需使用FunctionSupplier)。

interface IFunction<T, R> {

    R apply(T t);

}
interface ISupplier<T> {

    T get();

}

接下来,如果给定的JSON设计得很好,则必须“取消”应该是数组的属性。 下面的extract方法尝试迭代每个对象属性并检查它是否可以提取索引。 如果提取了索引,他们会尝试确保列表大小足以容纳所有元素。 一旦列表大小合适,他们就会尝试通过策略从其他地方获取值。

final class Extractors {

    private Extractors() {
    }

    static <R> List<R> extract(
            final JsonObject jsonObject,
            final IFunction<? super String, Integer> scanner,
            final IFunction<? super JsonElement, ? extends R> mapper
    ) {
        final List<R> list = new ArrayList<>();
        for ( final Map.Entry<String, JsonElement> entry : jsonObject.entrySet() ) {
            final Integer i = scanner.apply(entry.getKey());
            if ( i != null ) {
                ensureSize(list, i, () -> null);
                list.set(i - 1, mapper.apply(entry.getValue()));
            }
        }
        return list;
    }

    static <R> List<List<R>> extract(
            final JsonObject jsonObject,
            final IFunction<? super String, Integer> superScanner,
            final IFunction<? super String, Integer> subScanner,
            final IFunction<? super JsonElement, ? extends R> mapper
    ) {
        final List<List<R>> superList = new ArrayList<>();
        for ( final Map.Entry<String, JsonElement> entry : jsonObject.entrySet() ) {
            final Integer superI = superScanner.apply(entry.getKey());
            if ( superI != null ) {
                final Integer subI = subScanner.apply(entry.getKey());
                if ( subI != null ) {
                    ensureSize(superList, superI, ArrayList::new);
                    final List<R> subList = superList.get(superI - 1);
                    ensureSize(subList, subI, () -> null);
                    subList.set(subI - 1, mapper.apply(entry.getValue()));
                }
            }
        }
        return superList;
    }

    private static <T> void ensureSize(final Collection<? super T> collection, final int size, final ISupplier<? extends T> defaultValueSupplier) {
        while ( collection.size() < size ) {
            collection.add(defaultValueSupplier.get());
        }
    }

}

下一个类表示一些字符串到整数的映射策略工厂方法,以便在上面的extract方法中使用:

final class Scanners {

    private Scanners() {
    }

    static IFunction<String, Integer> scanByPattern(final String pattern) {
        return scanByPattern(Pattern.compile(pattern));
    }

    static IFunction<String, Integer> scanByPattern(final Pattern pattern) {
        return name -> {
            final Matcher matcher = pattern.matcher(name);
            if ( !matcher.matches() ) {
                return null;
            }
            return parseInt(matcher.group(1));
        };
    }

}

那些很容易的部分。 编写自定义反序列化器可能会更复杂。

final class ExerciseJsonDeserializer
        implements JsonDeserializer<Exercise> {

    private static final JsonDeserializer<Exercise> exerciseJsonDeserializer = new ExerciseJsonDeserializer();

    private static final IFunction<String, Integer> questionScanner = scanByPattern("question(\\d+)");
    private static final IFunction<String, Integer> correctAnswerScanner = scanByPattern("correctAnswer(\\d+)");
    private static final IFunction<String, Integer> answerScanner1 = scanByPattern("answer(\\d+)_\\d+");
    private static final IFunction<String, Integer> answerScanner2 = scanByPattern("answer\\d+_(\\d+)");

    private ExerciseJsonDeserializer() {
    }

    static JsonDeserializer<Exercise> getExerciseJsonDeserializer() {
        return exerciseJsonDeserializer;
    }

    @Override
    public Exercise deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final JsonObject jsonObject = jsonElement.getAsJsonObject();
        return new Exercise(
                jsonObject.getAsJsonPrimitive("title").getAsString(),
                mergeQuestionParts(
                        extract(jsonObject, questionScanner, JsonElement::getAsString),
                        extract(jsonObject, answerScanner1, answerScanner2, JsonElement::getAsString),
                        extract(jsonObject, correctAnswerScanner, JsonElement::getAsString)
                )
        );
    }

    private static List<Question> mergeQuestionParts(final Iterable<String> questionTitles, final Iterable<List<String>> answers,
            final Iterable<String> correctAnswers) {
        final Iterator<String> questionIterator = questionTitles.iterator();
        final Iterator<List<String>> answerIterator = answers.iterator();
        final Iterator<String> correctAnswerIterator = correctAnswers.iterator();
        final List<Question> questions = new ArrayList<>();
        while ( questionIterator.hasNext() && answerIterator.hasNext() && correctAnswerIterator.hasNext() ) {
            final String question = questionIterator.next();
            final List<String> rawAnswers = answerIterator.next();
            final String correctAnswer = correctAnswerIterator.next();
            questions.add(new Question(question, rawAnswers, rawAnswers.indexOf(correctAnswer)));
        }
        return questions;
    }
}
final class ExerciseWrapperJsonDeserializer
        implements JsonDeserializer<ExerciseWrapper> {

    private static final JsonDeserializer<ExerciseWrapper> exerciseWrapperJsonDeserializer = new ExerciseWrapperJsonDeserializer();

    private static final IFunction<String, Integer> exerciseScanner = scanByPattern("exercise(\\d+)");

    private ExerciseWrapperJsonDeserializer() {
    }

    static JsonDeserializer<ExerciseWrapper> getExerciseWrapperJsonDeserializer() {
        return exerciseWrapperJsonDeserializer;
    }

    @Override
    public ExerciseWrapper deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
            throws JsonParseException {
        final JsonObject jsonObject = jsonElement.getAsJsonObject();
        return new ExerciseWrapper(
                extract(jsonObject.getAsJsonObject("exercises"), exerciseScanner, je -> context.deserialize(je, Exercise.class))
        );
    }

}

它如何在Java 8中使用:

private static final Gson gson = new GsonBuilder()
        .registerTypeAdapter(ExerciseWrapper.class, getExerciseWrapperJsonDeserializer())
        .registerTypeAdapter(Exercise.class, getExerciseJsonDeserializer())
        .setPrettyPrinting()
        .create();

public static void main(final String... args)
        throws IOException {
    try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44165271.class, "exercise.json") ) {
        final ExerciseWrapper exerciseWrapper = gson.fromJson(jsonReader, ExerciseWrapper.class);
        exerciseWrapper.exercises.forEach(exercise -> {
            System.out.println(exercise.title);
            exercise.questions.forEach(question -> {
                System.out.println("\t" + question.question);
                question.answers.forEach(answer -> System.out.println("\t\t" + answer));
                System.out.println("\t" + (question.correctAnswer + 1));
            });
        });
        System.out.println("-----");
        System.out.println(gson.toJson(exerciseWrapper));
    }
}

输出:

Ejercicio de comprensión B.1 (Comprehension B.1)
    La noticia habla...
        de un test para evaluar la inteligencia humana.
        de un descubrimiento científico relacionado con el ser humano.
        del descubrimiento de una nueva especie animal.
    2
    Los genes identificados...
        pueden explicar algunos comportamientos sociales del ser humano.
        explican el funcionamiento completo del cerebro.
        están relacionados con la inteligencia.
    3
    Este descubrimiento...
        no aportará nada a las investigaciones sobre el cerebro.
        podría ofrecer nuevos datos sobre el funcionamiento del cerebro.
        no es muy importante para la biología.
    2
    Según el texto, los científicos ya saben todo sobre los procesos cognitivos.
        No se sabe.
        Verdadero.
        Falso.
    3
Ejercicio de vocabulario B.2 (Vocabulary B.2)
    Relacionas 'cognitivo' con...
        la empatía.
        el conocimiento.
        una congestión.
    2
    'Cohorte' es igual que...
        'conjunto'.
        'comprensión'.
        'finalización'.
    1
    Lo contrario de 'específico' es...
        'genérico'.
        'aleatorio'.
        'concreto'.
    1
    El 'genoma' está relacionado con el ADN.
        No se sabe.
        Falso.
        Verdadero.
    3
-----
{
  "exercises": [
    {
      "title": "Ejercicio de comprensión B.1 (Comprehension B.1)",
      "questions": [
        {
          "question": "La noticia habla...",
          "answers": [
            "de un test para evaluar la inteligencia humana.",
            "de un descubrimiento científico relacionado con el ser humano.",
            "del descubrimiento de una nueva especie animal."
          ],
          "correctAnswer": 1
        },
        {
          "question": "Los genes identificados...",
          "answers": [
            "pueden explicar algunos comportamientos sociales del ser humano.",
            "explican el funcionamiento completo del cerebro.",
            "están relacionados con la inteligencia."
          ],
          "correctAnswer": 2
        },
        {
          "question": "Este descubrimiento...",
          "answers": [
            "no aportará nada a las investigaciones sobre el cerebro.",
            "podría ofrecer nuevos datos sobre el funcionamiento del cerebro.",
            "no es muy importante para la biología."
          ],
          "correctAnswer": 1
        },
        {
          "question": "Según el texto, los científicos ya saben todo sobre los procesos cognitivos.",
          "answers": [
            "No se sabe.",
            "Verdadero.",
            "Falso."
          ],
          "correctAnswer": 2
        }
      ]
    },
    {
      "title": "Ejercicio de vocabulario B.2 (Vocabulary B.2)",
      "questions": [
        {
          "question": "Relacionas \u0027cognitivo\u0027 con...",
          "answers": [
            "la empatía.",
            "el conocimiento.",
            "una congestión."
          ],
          "correctAnswer": 1
        },
        {
          "question": "\u0027Cohorte\u0027 es igual que...",
          "answers": [
            "\u0027conjunto\u0027.",
            "\u0027comprensión\u0027.",
            "\u0027finalización\u0027."
          ],
          "correctAnswer": 0
        },
        {
          "question": "Lo contrario de \u0027específico\u0027 es...",
          "answers": [
            "\u0027genérico\u0027.",
            "\u0027aleatorio\u0027.",
            "\u0027concreto\u0027."
          ],
          "correctAnswer": 0
        },
        {
          "question": "El \u0027genoma\u0027 está relacionado con el ADN.",
          "answers": [
            "No se sabe.",
            "Falso.",
            "Verdadero."
          ],
          "correctAnswer": 2
        }
      ]
    }
  ]
}