我正在用gson编写json文件(在Java中),但我还想把它们读回去。(其中的键只有在运行时才知道),所以我想使用一个pair类来表示它们,我目前使用的是org.apache.commons.lang3.tuple中的pair类,但它没有像gson那样的noArg构造函数,需要将数据读回,因此当我执行这一步时,代码会失败。是否有其他的类可以更好地工作?下面是我想要的代码的一个小版本。
import org.apache.commons.lang3.tuple.Pair;
import com.google.gson.GsonBuilder;
import com.google.gson.GsonBuilder;
class JsonFile { ArrayList<Event> events;
Public void diff(JsonFile other) {
// code not shown, but checks some things for being equal
}
}
class Event { String type; // of event
Parameters params;
Public Event(String type, Parameters params) {
this.type = type;
this.params = params;
}
}
class Parameters extends ArrayList<Pair<String, Object>> {}
JsonFile eventsToWrite; // populated by code
JsonFile eventstoDiff; // a stored copy of what the file "should" look like
// ...
// Some code filling eventsToWrite
Parameters params = new Parameters();
params.add(Pair.of("xyzzy", "is a magic name"));
params.add(Pair.of("foo", "bar");
Event event = new Event("lispy event", params);
eventsToWrite.add(event);
// ...
Reader reader new BufferedReader(new FileReader(new File("toDiff.json")));
GsonBuilder bld = new GsonBuilder();
Gson gsonRead = bld.create();
eventsToDiff = gsonRead.fromJson(reader, JsonFile.class); // this fails
eventsToWrite.diff(eventsToDiff);
4条答案
按热度按时间zzlelutf1#
我看到几个选项:
1.您可以使用
MutablePair
代替Pair
。它应该可以立即使用。1.您可以使用
Map<String,Object>
而不是ArrayList<Pair<String, Object>>
。此解决方案仅在参数键唯一时有效。1.您可以创建自己的
Parameter
类。1.如果您坚持使用
Pair
,并且希望保持它的不可变性(实际上这是有意义的),则需要实现自己的TypeAdapter。1.最后但同样重要的是:请使用Jackson而不是Gson。
omvjsjqw2#
TL;DR:Gson不能使用
Pair
,因为没有一种方法可以示例化它(至少Gson知道:除非您告诉Gson使用Pair.of
)。很抱歉,您的解决方案存在一些问题。
List
的声明站点绑定到ArrayList
。ArrayList
扩展Parameters
是一个相当糟糕的选择(ArrayList
有那么特殊吗?如果将来需要LinkedList
怎么办?如果需要Set
怎么办?哪一组?)--更喜欢聚合和封装集合。Pair
可以替换为Map.Entry
--尽可能使用最通用的类型(类似于前一项)。List
可能比使用Map
更好(如果需要以多Map方式存储多个键呢?)Object
,因为 * 没有办法从JSON重新构造 * 原始对象(Gson如何知道要反序列化的类有成千上万?哪一个更好?为什么要花费启发式或将类型标记直接放入JSON中,让Gson处理原始对象类(如果可能,包括参数化)?)--有关类似问题的更多评论,请参阅“当对象是另一个对象的字段时,为什么GSON无法转换对象?”。让它与Gson一起工作的一个可能的方法是迁移到字符串-字符串对(这样Gson就可以知道 * 如何 * 为键和值进行JSON往返),并编写一个感知对的类型适配器工厂。
上面的代码非常简单,它还使用数组将对打包到中,因为您可能希望存储重复的键并保存一些空间(此外,两元素数组很可能是对的良好载体)。
第一个
中间JSON如下所示:
使用Gson的另一个想法(有点疯狂)是为
Object
实现一个启发式类型适配器,该适配器使用自定义的类型使用注解进行注解,比如@Heuristics
(Java 8沿着了这种注解)。Pair<String, @Heuristics Object>
,但据我所知,Gson无法在类型标记内携带类型使用注解,因此类型适配器工厂无法获取它们并应用自定义类型适配器(注意:为Object
实现这样的类型适配器(即使是那些未注解的类型适配器)也可能导致不希望的全局效果)。根据您的新信息,I * 可能 * 是这样实现的(没有太多的注解,但如果有更多的其他需求,可以重新实现):
第一个问题
pkwftd7m3#
如果你检查
Pair
source code,你会发现它是一个抽象类,没有字段,只有一些getter和setter。GSON需要字段(抽象类或接口不能被示例化,所以在反序列化时会失败)。See this for example。Mafor's answer建议的第一个选项MutablePair是
abstract class Pair
的一个实现,它有Gson要使用的字段。这是一个可行的选择。
Jackson可以使用getter和setter。但是,如果您将类型声明为
Pair
而不是MutablePair
,没有setter(也没有字段),那么反序列化可能仍然会失败。因此Jackson可能不是唯一的答案。如果您使用MutablePair
,Gson也可以工作。我认为您应该考虑创建自己
MyPair
类,而不是试图找到一些现有的“标准”或者重构您的设计,以便在没有冲突键或出现冲突键时使用
Map
,例如,使用接口MultiValuedMap
的一些合适的实现4urapxun4#
我最终得到的(在与我的客户讨论之后)基本上是这样的:
Map只Map一个东西,所以@Mafor建议的MutablePair可能会更好,但是我的客户要求使用Map,尽管它每个Map只有一个对,这是矫枉过正。
此外,正如与@fluffy讨论的那样,我使用ArrayList为diff中的代码获取.iterator()成员函数,该函数按顺序比较参数(相同的参数按不同的顺序表示其他含义(例如,copy A B与copy B A不同)。