如何对字符串和子字符串数组(对象?)进行Gson序列化

huangapple go评论60阅读模式
英文:

How to serialize strings and sub-array (object?) of strings gson

问题

Beginner!

我有一个测验应用程序 完整代码在Github上,它从 json 中加载带有四个参数的 List(之前的数据存储在 xml 中):

  1. 问题
  2. 图像
  3. 四个可能的答案,以 radioGroup(子列表)的形式呈现
  4. 正确的答案
   [{
		"question": "谁是“现代爱情”摇滚歌手?",
		"imageUrl": "https://postimg.cc/2VL1Y1jd",
		"answerOptions": [{
			"1": "杰米·亨德里克斯",
			"2": "大卫·鲍伊",
			"3": "吉姆·莫里森",
			"4": "埃尔维斯·普雷斯利"
		}],
		"correctAnswer": "大卫·鲍伊"
	}]

我得到了错误:

> java.lang.IllegalStateException: 在第1行第118列处期望是字符串,但实际是 BEGIN_OBJECT,在路径 $[0].answerOptions[0]

这是因为我已经将所有请求的数据类型声明为字符串,而明显我有一个混合类型的数组(一个对象和一个数组):

@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
     if (response.isSuccessful()) {
          String string = response.body().string();
          Gson gson = new Gson();
          Type type = new TypeToken<List<Quiz>>(){}.getType();
          List<Quiz> list = gson.fromJson(string, type);

          // 保存到数据库
          for (int i = 0; i < list.size(); i++) {
              Quiz quiz = list.get(i);
              quiz.save();
          }
...

我可以在桥接类中实现 此处 的解决方案:

class Quiz extends LitePalSupport {
    String question;
    String imageUrl;
    String [] answerOptions;
    String correctAnswer;

    Quiz(String question, String imageUrl, String [] answerOptions, String correctAnswer) {
        this.question = question;
        this.imageUrl = imageUrl;
        this.answerOptions = answerOptions;
        this.correctAnswer = correctAnswer;
    }
}

但这意味着我将会向适配器视图持有者发送一个子类,而该持有者期望的是 String []

...
quizHolder.createRadioButtons(quiz.answerOptions);
...
void createRadioButtons(String[] answerOptions) {
    if (radioGroup.getChildAt(0) != null)
        radioGroup.removeAllViews();
    for (String s : answerOptions) {
        radioGroup.addView(createRadioButtonAnswerAndSetOnClickListener(s));
    }
}
...

是否有办法在不创建子类的情况下从 json 中获取子列表?

编辑:

根据 @andy-turner 的推荐更改后,我得到了错误:

> org.litepal.exceptions.LitePalSupportException: 尝试在空对象引用上调用虚拟方法 'java.lang.Class[] java.lang.reflect.Constructor.getParameterTypes()'

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.celebrityquiz, PID: 6493
    org.litepal.exceptions.LitePalSupportException: 尝试在空对象引用上调用虚拟方法 'java.lang.Class[] java.lang.reflect.Constructor.getParameterTypes()'
        at org.litepal.crud.DataHandler.query(DataHandler.java:154)
        at org.litepal.crud.QueryHandler.onFindAll(QueryHandler.java:123)
        at org.litepal.Operator.findAll(Operator.java:1117)
        at org.litepal.Operator.findAll(Operator.java:1082)
        at org.litepal.LitePal.findAll(LitePal.java:798)
        at com.example.celebrityquiz.MainActivity$1$2.run(MainActivity.java:115)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6077)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
...

这可能是在我尝试从数据库获取数据时调用的:

List<Quiz> list = LitePal.findAll(Quiz.class);

answerOptions 列也没有显示出来。

如何对字符串和子字符串数组(对象?)进行Gson序列化

英文:

Beginner!

I have quiz app full code on Github that loads a List with four arguments from json (data was previously stored in xml):

  1. question
  2. image
  3. four possible answers presented in a radioGroup (sub-List)
  4. correct answer
   [{
&quot;question&quot;: &quot;Who is the &#39;Modern Love&#39; rock star singer?&quot;,
&quot;imageUrl&quot;: &quot;https://postimg.cc/2VL1Y1jd&quot;,
&quot;answerOptions&quot;: [{
&quot;1&quot;: &quot;Jaimie Hendrix&quot;,
&quot;2&quot;: &quot;David Bowie&quot;,
&quot;3&quot;: &quot;Jim Morrison&quot;,
&quot;4&quot;: &quot;Elvis Presley&quot;
}],
&quot;correctAnswer&quot;: &quot;David Bowie&quot;
}]

I'm getting the error

> java.lang.IllegalStateException: Expected a string but was BEGIN_OBJECT at line 1 column 118 path $[0].answerOptions[0]

and that's because I have declared all datatyped requested as strings while clearly I have an array of mixed types (an object and an array):

@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()) {
String string = response.body().string();
Gson gson = new Gson();
Type type = new TypeToken&lt;List&lt;Quiz&gt;&gt;(){}.getType();
List&lt;Quiz&gt; list = gson.fromJson(string, type);
// Save to database
for (int i = 0; i &lt; list.size(); i++) {
Quiz quiz = list.get(i);
quiz.save();
}
...

I could implement the solution here in the bridge class:

class Quiz extends LitePalSupport {
String question;
String imageUrl;
String [] answerOptions;
String correctAnswer;
Quiz(String question, String imageUrl, String [] answerOptions, String correctAnswer) {
this.question = question;
this.imageUrl = imageUrl;
this.answerOptions = answerOptions;
this.correctAnswer = correctAnswer;
}
}

but that means I'll be sending a SubClass to the adapter view holder which is expecting String []

...
quizHolder.createRadioButtons(quiz.answerOptions);
...
void createRadioButtons(String[] answerOptions) {
if (radioGroup.getChildAt(0) != null)
radioGroup.removeAllViews();
for (String s : answerOptions) {
radioGroup.addView(createRadioButtonAnswerAndSetOnClickListener(s));
}
}
...

Is there a way I could get the sub-list from json without creating a subclass?


Edit:

After the recommended change by @andy-turner I'm getting the error:

> org.litepal.exceptions.LitePalSupportException: Attempt to invoke virtual method 'java.lang.Class[] java.lang.reflect.Constructor.getParameterTypes()' on a null object reference

E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.celebrityquiz, PID: 6493
org.litepal.exceptions.LitePalSupportException: Attempt to invoke virtual method &#39;java.lang.Class[] java.lang.reflect.Constructor.getParameterTypes()&#39; on a null object reference
at org.litepal.crud.DataHandler.query(DataHandler.java:154)
at org.litepal.crud.QueryHandler.onFindAll(QueryHandler.java:123)
at org.litepal.Operator.findAll(Operator.java:1117)
at org.litepal.Operator.findAll(Operator.java:1082)
at org.litepal.LitePal.findAll(LitePal.java:798)
at com.example.celebrityquiz.MainActivity$1$2.run(MainActivity.java:115)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
...

probably invoked when I try to get the data from the databse:

List&lt;Quiz&gt; list = LitePal.findAll(Quiz.class);

And the answerOptions column isn't showing up either

如何对字符串和子字符串数组(对象?)进行Gson序列化

答案1

得分: 2

answerOptions 应该是一个 String[] 类型,但是你将它指定为 Object[] 类型(因为使用了 {}):

"answerOptions": [{
    ...
}],

将它改为字符串数组:

"answerOptions": [
    "Jaimie Hendrix",
    "David Bowie",
    "Jim Morrison",
    "Elvis Presley"
],

附注 - 应该是 "Jimi",而不是 "Jaimie"。

英文:

answerOptions is expected to be a String[], but you've specified it as Object[] (because of the {}):

    &quot;answerOptions&quot;: [{
...
}],

Make it an array of strings:

    &quot;answerOptions&quot;: [
&quot;Jaimie Hendrix&quot;,
&quot;David Bowie&quot;,
&quot;Jim Morrison&quot;,
&quot;Elvis Presley&quot;
],

PS - "Jimi", rather than "Jaimie".

答案2

得分: 1

package pl.jac.mija.gson;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class QuizTestChangeJson {
  @Test
  public void changeJson() {
    //given
    String json = getNewJsonOneProposeOptions();
    Type type = new TypeToken<List<Quiz>>() {
    }.getType();
    //when
    List<Quiz> list = new Gson().fromJson(json, type);
    //then
    assertEquals(1, list.size());
    String correctAnswer = list.get(0).correctAnswer;
    assertEquals(correctAnswer, list.get(0).answerOptions[1]);
    assertEquals(1, Arrays.asList(list.get(0).answerOptions).indexOf(correctAnswer)); // key is string
  }

  @NotNull
  private String getNewJsonOneProposeOptions() {
    return "   [{\n" +
            "        \"question\": \"Who is the 'Modern Love' rock star singer?\",\n" +
            "        \"imageUrl\": \"https://postimg.cc/2VL1Y1jd\",\n" +
            "        \"answerOptions\": [\n" +
            "             \"Jaimie Hendrix\",\n" +
            "             \"David Bowie\",\n" +
            "             \"Jim Morrison\",\n" +
            "             \"Elvis Presley\"\n" +
            "        ],\n" +
            "        \"correctAnswer\": \"David Bowie\"\n" +
            "    }]";
  }
}

class Quiz {
  String question;
  String imageUrl;
  String[] answerOptions;
  String correctAnswer;
}

// Change Model Quiz to Json
// The first version is with the key String -> QuizV2_String
// The second version is with the key Long -> QuizV2_Long
// I added a test to pull the correct answer key

package pl.jac.mija.gson;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;

import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;

public class QuizTestChangeModel {
  @Test
  public void changeModelQuizKeyString() {
    //given
    String json = getJsonOriginal();
    Type type = new TypeToken<List<QuizV2_String>>() {
    }.getType();
    //when
    List<QuizV2_String> list = new Gson().fromJson(json, type);
    //then
    assertEquals(1, list.size());
    assertEquals("David Bowie", list.get(0).answerOptions.get(0).get("2")); // key is string
  }

  @Test
  public void changeModelQuizKeyLong() {
    //given
    String json = getJsonOriginal();
    Type type = new TypeToken<List<QuizV2_Long>>() {
    }.getType();
    //when
    List<QuizV2_Long> list = new Gson().fromJson(json, type);
    //then
    assertEquals(1, list.size());
    assertEquals("David Bowie", list.get(0).answerOptions.get(0).get(2L)); // key is long
  }

  @Test
  public void changeModelQuizFindAnswer() {
    //given
    String json = getJsonOriginal();
    Type type = new TypeToken<List<QuizV2_String>>() {
    }.getType();
    //when
    List<QuizV2_String> list = new Gson().fromJson(json, type);
    //then
    String correctAnswer = list.get(0).correctAnswer;
    String key = list.get(0).answerOptions.get(0).entrySet().stream()
            .filter(x -> correctAnswer.equals(x.getValue()))
            .map(Map.Entry::getKey)
            .findFirst()
            .orElse(null);
    assertEquals("2", key);
    assertEquals("David Bowie", list.get(0).answerOptions.get(0).get(key));
  }

  @NotNull
  private String getJsonOriginal() {
    return "   [{\n" +
            "        \"question\": \"Who is the 'Modern Love' rock star singer?\",\n" +
            "        \"imageUrl\": \"https://postimg.cc/2VL1Y1jd\",\n" +
            "        \"answerOptions\": [{\n" +
            "            \"1\": \"Jaimie Hendrix\",\n" +
            "            \"2\": \"David Bowie\",\n" +
            "            \"3\": \"Jim Morrison\",\n" +
            "            \"4\": \"Elvis Presley\"\n" +
            "        }],\n" +
            "        \"correctAnswer\": \"David Bowie\"\n" +
            "    }]";
  }
}

class QuizV2_String {
  String question;
  String imageUrl;
  List<Map<String, String>> answerOptions;
  String correctAnswer;
}

class QuizV2_Long {
  String question;
  String imageUrl;
  List<Map<Long, String>> answerOptions;
  String correctAnswer;
}
英文:

my propo

-> change Json replace List (Objects or Map) to List Strings

package pl.jac.mija.gson;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.assertEquals;
public class QuizTestChangeJson {
@Test
public void changeJson() {
//given
String json = getNewJsonOneProposeOptions();
Type type = new TypeToken&lt;List&lt;Quiz&gt;&gt;() {
}.getType();
//when
List&lt;Quiz&gt; list = new Gson().fromJson(json, type);
//then
assertEquals(1, list.size());
String correctAnswer = list.get(0).correctAnswer;
assertEquals(correctAnswer, list.get(0).answerOptions[1]);
assertEquals(1, Arrays.asList(list.get(0).answerOptions).indexOf(correctAnswer)); // key is string
}
@NotNull
private String getNewJsonOneProposeOptions() {
return &quot;   [{\n&quot; +
&quot;        \&quot;question\&quot;: \&quot;Who is the &#39;Modern Love&#39; rock star singer?\&quot;,\n&quot; +
&quot;        \&quot;imageUrl\&quot;: \&quot;https://postimg.cc/2VL1Y1jd\&quot;,\n&quot; +
&quot;        \&quot;answerOptions\&quot;: [\n&quot; +
&quot;             \&quot;Jaimie Hendrix\&quot;,\n&quot; +
&quot;             \&quot;David Bowie\&quot;,\n&quot; +
&quot;             \&quot;Jim Morrison\&quot;,\n&quot; +
&quot;             \&quot;Elvis Presley\&quot;\n&quot; +
&quot;        ],\n&quot; +
&quot;        \&quot;correctAnswer\&quot;: \&quot;David Bowie\&quot;\n&quot; +
&quot;    }]&quot;;
}
}
class Quiz {
String question;
String imageUrl;
String[] answerOptions;
String correctAnswer;
}

change Model Quiz to Json

the first version is with the key String -> QuizV2_String

the second version is with the key Long -> QuizV2_Long

I added a test to pull the correct answer key

 package pl.jac.mija.gson;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class QuizTestChangeModel {
@Test
public void changeModelQuizKeyString() {
//given
String json = getJsonOriginal();
Type type = new TypeToken&lt;List&lt;QuizV2_String&gt;&gt;() {
}.getType();
//when
List&lt;QuizV2_String&gt; list = new Gson().fromJson(json, type);
//then
assertEquals(1, list.size());
assertEquals(&quot;David Bowie&quot;, list.get(0).answerOptions.get(0).get(&quot;2&quot;)); // key is string
}
@Test
public void changeModelQuizKeyLong() {
//given
String json = getJsonOriginal();
Type type = new TypeToken&lt;List&lt;QuizV2_Long&gt;&gt;() {
}.getType();
//when
List&lt;QuizV2_Long&gt; list = new Gson().fromJson(json, type);
//then
assertEquals(1, list.size());
assertEquals(&quot;David Bowie&quot;, list.get(0).answerOptions.get(0).get(2L)); // key is string
}
@Test
public void changeModelQuizFindAnswer() {
//given
String json = getJsonOriginal();
Type type = new TypeToken&lt;List&lt;QuizV2_String&gt;&gt;() {
}.getType();
//when
List&lt;QuizV2_String&gt; list = new Gson().fromJson(json, type);
//then
String correctAnswer = list.get(0).correctAnswer;
String key = list.get(0).answerOptions.get(0).entrySet().stream().filter(x -&gt; correctAnswer.equals(x.getValue())).map(Map.Entry::getKey).findFirst().orElse(null);
assertEquals(&quot;2&quot;, key);
assertEquals(&quot;David Bowie&quot;, list.get(0).answerOptions.get(0).get(key));
}
@NotNull
private String getJsonOriginal() {
return &quot;   [{\n&quot; +
&quot;        \&quot;question\&quot;: \&quot;Who is the &#39;Modern Love&#39; rock star singer?\&quot;,\n&quot; +
&quot;        \&quot;imageUrl\&quot;: \&quot;https://postimg.cc/2VL1Y1jd\&quot;,\n&quot; +
&quot;        \&quot;answerOptions\&quot;: [{\n&quot; +
&quot;            \&quot;1\&quot;: \&quot;Jaimie Hendrix\&quot;,\n&quot; +
&quot;            \&quot;2\&quot;: \&quot;David Bowie\&quot;,\n&quot; +
&quot;            \&quot;3\&quot;: \&quot;Jim Morrison\&quot;,\n&quot; +
&quot;            \&quot;4\&quot;: \&quot;Elvis Presley\&quot;\n&quot; +
&quot;        }],\n&quot; +
&quot;        \&quot;correctAnswer\&quot;: \&quot;David Bowie\&quot;\n&quot; +
&quot;    }]&quot;;
}
}
class QuizV2_String {
String question;
String imageUrl;
List&lt;Map&lt;String, String&gt;&gt; answerOptions;
String correctAnswer;
}
class QuizV2_Long {
String question;
String imageUrl;
List&lt;Map&lt;Long, String&gt;&gt; answerOptions;
String correctAnswer;
}

huangapple
  • 本文由 发表于 2020年4月6日 05:09:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/61049536.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定