如果我想用gson从一个json文件中读取pair对象,我应该使用什么java类?

py49o6xq  于 2022-11-06  发布在  Java
关注(0)|答案(4)|浏览(352)

我正在用gson编写json文件(在Java中),但我还想把它们读回去。(其中的键只有在运行时才知道),所以我想使用一个pair类来表示它们,我目前使用的是org.apache.commons.lang3.tuple中的pair类,但它没有像gson那样的noArg构造函数,需要将数据读回,因此当我执行这一步时,代码会失败。是否有其他的类可以更好地工作?下面是我想要的代码的一个小版本。

  1. import org.apache.commons.lang3.tuple.Pair;
  2. import com.google.gson.GsonBuilder;
  3. import com.google.gson.GsonBuilder;
  4. class JsonFile { ArrayList<Event> events;
  5. Public void diff(JsonFile other) {
  6. // code not shown, but checks some things for being equal
  7. }
  8. }
  9. class Event { String type; // of event
  10. Parameters params;
  11. Public Event(String type, Parameters params) {
  12. this.type = type;
  13. this.params = params;
  14. }
  15. }
  16. class Parameters extends ArrayList<Pair<String, Object>> {}
  17. JsonFile eventsToWrite; // populated by code
  18. JsonFile eventstoDiff; // a stored copy of what the file "should" look like
  19. // ...
  20. // Some code filling eventsToWrite
  21. Parameters params = new Parameters();
  22. params.add(Pair.of("xyzzy", "is a magic name"));
  23. params.add(Pair.of("foo", "bar");
  24. Event event = new Event("lispy event", params);
  25. eventsToWrite.add(event);
  26. // ...
  27. Reader reader new BufferedReader(new FileReader(new File("toDiff.json")));
  28. GsonBuilder bld = new GsonBuilder();
  29. Gson gsonRead = bld.create();
  30. eventsToDiff = gsonRead.fromJson(reader, JsonFile.class); // this fails
  31. eventsToWrite.diff(eventsToDiff);
zzlelutf

zzlelutf1#

我看到几个选项:
1.您可以使用MutablePair代替Pair。它应该可以立即使用。
1.您可以使用Map<String,Object>而不是ArrayList<Pair<String, Object>>。此解决方案仅在参数键唯一时有效。
1.您可以创建自己的Parameter类。
1.如果您坚持使用Pair,并且希望保持它的不可变性(实际上这是有意义的),则需要实现自己的TypeAdapter
1.最后但同样重要的是:请使用Jackson而不是Gson。

omvjsjqw

omvjsjqw2#

TL;DR:Gson不能使用Pair,因为没有一种方法可以示例化它(至少Gson知道:除非您告诉Gson使用Pair.of)。
很抱歉,您的解决方案存在一些问题。

  • 不要在可以使用List的声明站点绑定到ArrayList
  • ArrayList扩展Parameters是一个相当糟糕的选择(ArrayList有那么特殊吗?如果将来需要LinkedList怎么办?如果需要Set怎么办?哪一组?)--更喜欢聚合和封装集合。
  • Pair可以替换为Map.Entry--尽可能使用最通用的类型(类似于前一项)。
  • 令人惊讶的是,对这样的对/条目使用List可能比使用Map更好(如果需要以多Map方式存储多个键呢?)
  • Gson,不幸的是我不会让您轻松地序列化/反序列化Object,因为 * 没有办法从JSON重新构造 * 原始对象(Gson如何知道要反序列化的类有成千上万?哪一个更好?为什么要花费启发式或将类型标记直接放入JSON中,让Gson处理原始对象类(如果可能,包括参数化)?)--有关类似问题的更多评论,请参阅“当对象是另一个对象的字段时,为什么GSON无法转换对象?”。

让它与Gson一起工作的一个可能的方法是迁移到字符串-字符串对(这样Gson就可以知道 * 如何 * 为键和值进行JSON往返),并编写一个感知对的类型适配器工厂。

  1. final class PairTypeAdapterFactory
  2. implements TypeAdapterFactory {
  3. private static final TypeAdapterFactory instance = new PairTypeAdapterFactory();
  4. private PairTypeAdapterFactory() {
  5. }
  6. static TypeAdapterFactory getInstance() {
  7. return instance;
  8. }
  9. @Override
  10. @Nullable
  11. public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
  12. if ( !Pair.class.isAssignableFrom(typeToken.getRawType()) ) {
  13. return null;
  14. }
  15. final ParameterizedType parameterizedType = (ParameterizedType) typeToken.getType();
  16. final Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
  17. final TypeAdapter<Pair<Object, Object>> pairTypeAdapter = resolveTypeAdapter(gson, actualTypeArguments[0], actualTypeArguments[1]);
  18. @SuppressWarnings("unchecked")
  19. final TypeAdapter<T> typeAdapter = (TypeAdapter<T>) pairTypeAdapter;
  20. return typeAdapter;
  21. }
  22. private <L, R> TypeAdapter<Pair<L, R>> resolveTypeAdapter(final Gson gson, final Type leftType, final Type rightType) {
  23. @SuppressWarnings("unchecked")
  24. final TypeAdapter<L> leftTypeAdapter = (TypeAdapter<L>) gson.getDelegateAdapter(this, TypeToken.get(leftType));
  25. @SuppressWarnings("unchecked")
  26. final TypeAdapter<R> rightTypeAdapter = (TypeAdapter<R>) gson.getDelegateAdapter(this, TypeToken.get(rightType));
  27. return new TypeAdapter<Pair<L, R>>() {
  28. @Override
  29. public void write(final JsonWriter out, final Pair<L, R> value)
  30. throws IOException {
  31. out.beginArray();
  32. leftTypeAdapter.write(out, value.getLeft());
  33. rightTypeAdapter.write(out, value.getRight());
  34. out.endArray();
  35. }
  36. @Override
  37. public Pair<L, R> read(final JsonReader in)
  38. throws IOException {
  39. in.beginArray();
  40. final L left = leftTypeAdapter.read(in);
  41. final R right = rightTypeAdapter.read(in);
  42. in.endArray();
  43. return Pair.of(left, right);
  44. }
  45. }
  46. .nullSafe();
  47. }
  48. }

上面的代码非常简单,它还使用数组将对打包到中,因为您可能希望存储重复的键并保存一些空间(此外,两元素数组很可能是对的良好载体)。
第一个
中间JSON如下所示:

  1. {
  2. "events": [
  3. {
  4. "type": "lispy event",
  5. "params": [
  6. [
  7. "xyzzy",
  8. "is a magic name"
  9. ],
  10. [
  11. "foo",
  12. "bar"
  13. ]
  14. ]
  15. }
  16. ]
  17. }

使用Gson的另一个想法(有点疯狂)是为Object实现一个启发式类型适配器,该适配器使用自定义的类型使用注解进行注解,比如@Heuristics(Java 8沿着了这种注解)。Pair<String, @Heuristics Object>,但据我所知,Gson无法在类型标记内携带类型使用注解,因此类型适配器工厂无法获取它们并应用自定义类型适配器(注意:为Object实现这样的类型适配器(即使是那些未注解的类型适配器)也可能导致不希望的全局效果)。
根据您的新信息,I * 可能 * 是这样实现的(没有太多的注解,但如果有更多的其他需求,可以重新实现):
第一个问题

展开查看全部
pkwftd7m

pkwftd7m3#

如果你检查Pair source code,你会发现它是一个抽象类,没有字段,只有一些getter和setter。GSON需要字段(抽象类或接口不能被示例化,所以在反序列化时会失败)。See this for example
Mafor's answer建议的第一个选项MutablePairabstract class Pair的一个实现,它有Gson要使用的字段。

  1. public L left;
  2. public R right;

这是一个可行的选择。

Jackson可以使用getter和setter。但是,如果您将类型声明为Pair而不是MutablePair,没有setter(也没有字段),那么反序列化可能仍然会失败。因此Jackson可能不是唯一的答案。如果您使用MutablePair,Gson也可以工作。

我认为您应该考虑创建自己MyPair类,而不是试图找到一些现有的“标准”

  1. @Getter
  2. @Setter
  3. public class MyPair {
  4. private String left;
  5. private Object right;
  6. }

或者重构您的设计,以便在没有冲突键或出现冲突键时使用Map,例如,使用接口MultiValuedMap的一些合适的实现

展开查看全部
4urapxun

4urapxun4#

我最终得到的(在与我的客户讨论之后)基本上是这样的:

  1. @NoArgsConstructor
  2. @AllArgsConstructor
  3. @EqualsAndHashCode
  4. final class Event {
  5. String type;
  6. List<Map<String, Object>> params;
  7. }

Map只Map一个东西,所以@Mafor建议的MutablePair可能会更好,但是我的客户要求使用Map,尽管它每个Map只有一个对,这是矫枉过正。
此外,正如与@fluffy讨论的那样,我使用ArrayList为diff中的代码获取.iterator()成员函数,该函数按顺序比较参数(相同的参数按不同的顺序表示其他含义(例如,copy A B与copy B A不同)。

相关问题