如何使用java gson解析动态json对象值

How to parse dynamic json object value with java gson

提问人:kennarddh 提问时间:10/27/2023 更新时间:10/27/2023 访问量:74

问:

我有2个班级

class X {
  public String name;
  public int age;
}
class Y {
  public float score;
}

json是这样的

{
  "type": "X",
  "data": {
    "name": "myname",
    "age": 20
  }
}

{
  "type": "Y",
  "data": {
    "score": 10
  }
}

如何使用 gson 解析 json,但如果类型字段是,则数据将是类实例XX

java json gson

评论

0赞 rjdkolb 10/27/2023
我不是投反对票的人,但到目前为止你尝试了什么?
0赞 kennarddh 10/27/2023
我没有..我不知道从哪里开始..
0赞 phuongnq1995 10/27/2023
我也不是,但我想我们可以轻松地使用(也带有包)到新的实例和 setter 变量。问题是我们只有一个类型实例,在大多数情况下,这并没有真正有用。ReflectionObject
1赞 Chaosfire 10/27/2023
编写自定义反序列化程序,如何为 Gson 编写自定义 JSON 反序列化程序? 应该能让你入门。

答:

1赞 Abra 10/27/2023 #1

您希望根据 type 元素的值反序列化 JSON。因此,您可以解析 JSON 并提取 type 元素的值。然后提取数据元素并使用 Gson 将其反序列化为 Java 对象。

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class GsonTest {

    public static void main(String[] args) {
        String json = """
{
  "type": "X",
  "data": {
    "name": "myname",
    "age": 20
  }
}
""";
        JsonElement root = JsonParser.parseString(json);
        JsonObject obj = root.getAsJsonObject();
        JsonElement typeElem = obj.get("type");
        String type = typeElem.getAsString();
        JsonElement dataElem = obj.get("data");
        Gson gson = new Gson();
        Class<?> theClass;
        if ("X".equals(type)) {
            theClass = X.class;
        }
        else if ("Y".equals(type)) {
            theClass = Y.class;
        }
        else {
            theClass = null;
        }
        if (theClass != null) {
            Object result = gson.fromJson(dataElem, theClass);
            System.out.println(result);
        }
    }
}

上面的代码使用了 Java 15 中添加的文本块

我在类中添加了一个 toString 方法:X

public String toString() {
    return String.format("%s:%d", name, age);
}

当我运行上面的代码时,我得到以下内容:

myname:20

请注意,通常会创建类和类 JavaBeans,即您需要添加“getters”和“setter”。XY

下面是一个完整的代码:

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class GsonTest {

    public static void main(String[] args) {
        String json = """
{
  "type": "Y",
  "data": {
    "score": 10
  }
}
""";
        JsonElement root = JsonParser.parseString(json);
        JsonObject obj = root.getAsJsonObject();
        JsonElement typeElem = obj.get("type");
        String type = typeElem.getAsString();
        JsonElement dataElem = obj.get("data");
        Gson gson = new Gson();
        Class<?> theClass;
        if ("X".equals(type)) {
            theClass = X.class;
        }
        else if ("Y".equals(type)) {
            theClass = Y.class;
        }
        else {
            theClass = null;
        }
        if (theClass != null) {
            Object result = gson.fromJson(dataElem, theClass);
            System.out.println(result);
        }
    }
}

class X {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return String.format("%s:%d", name, age);
    }
}

class Y {
    private float score;

    public float getScore() {
        return score;
    }

    public void setScore(float score) {
        this.score = score;
    }

    public String toString() {
        return Float.toString(score);
    }
}

请参阅 Gson 用户指南

评论

0赞 Marcono1234 10/28/2023
为了避免任何混淆:Gson 不需要 getter 和 setter 方法,实际上它只是忽略它们并直接访问字段。因此,它只取决于您的代码风格,或者您如何使用类,以及是否要添加 getter 和 setter。XY
0赞 kennarddh 10/27/2023 #2

我发现在谷歌吟游诗人的一些问题之后......

public static class CustomTypeAdapter extends TypeAdapter<Object> {
    @Override
    public void write(JsonWriter out, Object value) {

    }

    @Override
    public Object read(JsonReader reader) {
        JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject();

        String type = jsonObject.get("type").getAsString();

        System.out.println(type);

        return switch (type) {
            case "X" -> new Gson().fromJson(jsonObject.get("data"), X.class);
            case "Y" -> new Gson().fromJson(jsonObject.get("data"), Y.class);
            default -> throw new JsonParseException("Unknown type: " + type);
        };
    }
}

然后我像这样使用它

JsonReader reader = new Gson().newJsonReader(new StringReader("{\"type\":\"X\",\"data\":{\"name\":\"Name\",\"age\":10}}"));

Object output = new CustomTypeAdapter().read(reader);

System.out.println(((X) output).name);

但是有了这个,我不知道为什么我扩展了..我不认为我需要它。TypeAdapter<Object>

评论

1赞 Marcono1234 10/28/2023
虽然这可能会解决您的问题(目前),但它存在几个问题:创建效率低下且不继承原始实例设置的新实例,为空(至少它应该始终抛出异常以避免将来出现错误),首先有一个(使用您当前的代码,您可能可以直接使用里面的代码CustomTypeAdapterGsonGsonCustomTypeAdapter.writeTypeAdapterCustomTypeAdapter.read)
0赞 kennarddh 10/28/2023
@Marcono1234是的,我现在添加了 write 方法的代码,我不再扩展,并且每次读/写都要创建新实例,我可以通过在构造函数中缓存 Gson 实例来解决它吗?TypeAdapter<Object>
0赞 Marcono1234 10/28/2023
您可能可以应用@Abra的 anwser;在功能方面,它似乎完全相同
2赞 cloudchamb3r 10/27/2023 #3
var m = new TreeMap();
m.put("type", X.class.getSimpleName());
m.put("data", new X("myname", 20));

var gson = new Gson();
var json = gson.toJson(m);

如果您不想创建自定义适配器,请尝试使用 Map!
这可能是最简单的解决方案之一

评论

1赞 kennarddh 10/27/2023
这是为了写?我认为如此。我还需要写anw..泰