Java 以不区分顺序的方式使用Gson TypeAdapter反序列化JSON对象
是否有可能实现以下两个目标Java 以不区分顺序的方式使用Gson TypeAdapter反序列化JSON对象,java,json,gson,Java,Json,Gson,是否有可能实现以下两个目标 能够委托给调用自定义实现的默认Gson反序列化程序 不受JSON对象中键的不同顺序的影响 下面我将介绍两种可能的方法,它们只能实现其中一种 我正在使用的API要么返回一个成功的API,比如: { "type": "success", "data": { "display_name": "Invisible Pink Unicorn", "user_email": "user@example.com", "user_id": 123
- 能够委托给调用自定义实现的默认Gson反序列化程序
- 不受JSON对象中键的不同顺序的影响
我正在使用的API要么返回一个成功的API,比如:
{
"type": "success",
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "user@example.com",
"user_id": 1234
}
}
或者一个错误,比如:
{
"type": "error",
"data": {
"error_name": "incorrect_password",
"error_message": "The username or password you entered is incorrect."
}
}
目前处理它的方法是注册一个TypeAdapter,如果类型是“error”
,它会抛出一个异常,并显示给定的“error\u message”
:
我看不出有什么办法可以解决这个问题,因为我不认为JsonReader
有两次读取输入的选项,也没有办法在遇到“type”之后将“data”值缓存在一个抽象类型中,比如JsonElement
但实际上这是错误的,因为它不使用相同的Gson将工作委托给,所以它将使用不同的字段命名策略,例如
对。您应该使用JsonDeserializationContext
。。。因为我认为JsonReader没有两次读取输入的选项,所以也没有办法在遇到“type”之后将“data”值缓存在抽象类型(如JsonElement)中进行解析 对
JsonReader
是流阅读器,而JsonElement
是树。它们就像XML世界中的SAX和DOM,有各自的优缺点。流式读卡器只读取输入流,您必须自己缓冲/缓存中间数据
对于您的情况,您可以使用这两种方法,但为了简单起见,我选择了JsonDeserializer
(假设您不打算编写超快速的反序列化程序)
我不确定用户
和APIRERROR
之间的关系,但我会为两种不同类型的值使用一个通用类:真实值和错误。看起来您的两个类有一个共同的父类或祖先类,但我不确定在调用站点(可能是instanceof
?)如何处理它们。比方说,类似这样(构造函数隐藏以封装对象结构初始化的复杂性):
最终课程内容{
私有最终布尔访问;
私有最终T数据;
私人最终错误;
私有内容(最终布尔值isSuccess、最终T数据、最终ApiError错误){
this.issucess=issucess;
这个数据=数据;
this.error=错误;
}
静态内容成功(最终T数据){
返回新内容(true、data、null);
}
静态内容错误(最终ApiError错误){
返回新内容(false、null、error);
}
布尔值isSuccess(){
返回成功;
}
T getData()
抛出非法状态异常{
如果(!isSuccess){
抛出新的非法状态异常();
}
返回数据;
}
apirerror getError()
抛出非法状态异常{
如果(isSuccess){
抛出新的非法状态异常();
}
返回误差;
}
}
从我的角度来看,User
和apirerror
(虽然我更喜欢@SerializedName
对命名有更强的控制,但这似乎是一个习惯问题)
final类错误{
@SuppressWarnings(“错误名称”)
最终字符串errorName=null;
@SerializedName(“错误消息”)
最终字符串errorMessage=null;
}
最终类用户{
@SerializedName(“显示名称”)
最终字符串displayName=null;
@SerializedName(“用户\电子邮件”)
最终字符串userEmail=null;
@SuppressWarnings(“用户id”)
final int userId=Integer.valueOf(0);
}
接下来,由于树操作更简单,只需实现JSON反序列化器:
final类ContentJsonDeserializer
实现JsonDeserializer{
//此反序列化程序不保存任何状态
私有静态最终JsonDeserializer contentJsonDeserializer=new contentJsonDeserializer();
私有ContentJsonDeserializer(){
}
//…我们隐藏了这一事实,不允许在呼叫站点上实例化这一点
静态JsonDeserializer getContentJsonDeserializer(){
//缩小@SuppressWarnings作用域--抑制整个方法的警告可能是有害的
@抑制警告(“未选中”)
最终JsonDeserializer contentJsonDeserializer=(JsonDeserializer)contentJsonDeserializer.contentJsonDeserializer;
返回contentJsonDeserializer;
}
@凌驾
公共内容反序列化(最终JsonElement JsonElement、最终类型类型、最终JsonDeserializationContext上下文)
抛出JsonParseException{
final JsonObject JsonObject=jsoneelement.getAsJsonObject();
最终字符串responseType=jsonObject.getAsJsonPrimitive(“类型”).getAsString();
开关(响应类型){
“成功”案例:
返回成功(context.deserialize(jsonObject.get(“data”)、getTypeParameter0(type));
案例“错误”:
返回错误(context.deserialize(jsonObject.get(“数据”),apierro.class));
违约:
抛出新的JsonParseException(responseType);
}
}
//尝试为其第一个类型参数检测任何给定的类型参数化
私有静态类型getTypeParameter0(最终类型){
if(!(参数化类型的类型instanceof)){
返回Object.class;
}
返回((ParameteredType)类型).getActualTypeArguments()[0];
}
}
演示:
private static final Gson Gson=new GsonBuilder()
.registerTypeAdapter(Content.class,getContentJsonDeserializer())
.create();
私有静态最终类型userContent=newtypetoken(){
}.getType();
P
new GsonBuilder()
.registerTypeAdapter(User.class, new ContentDeserializer<User>())
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create()
public class ContentDeserializer<T> implements JsonDeserializer<T> {
@Override
public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
final JsonObject object = json.getAsJsonObject();
final String type = object.get("type").getAsString();
final JsonElement data = object.get("data");
final Gson gson = new Gson();
if ("error".equals(type)) {
throw gson.fromJson(data, ApiError.class);
} else {
return gson.fromJson(data, typeOfT);
}
}
}
public class UserAdapterFactory implements TypeAdapterFactory {
@SuppressWarnings("unchecked")
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (!User.class.isAssignableFrom(type.getRawType())) return null;
final TypeAdapter<User> userAdapter = (TypeAdapter<User>) gson.getDelegateAdapter(this, type);
final TypeAdapter<ApiError> apiErrorAdapter = gson.getAdapter(ApiError.class);
return (TypeAdapter<T>) new Adapter(userAdapter, apiErrorAdapter);
}
private static class Adapter extends TypeAdapter<User> {
private final TypeAdapter<User> userAdapter;
private final TypeAdapter<ApiError> apiErrorAdapter;
Adapter(TypeAdapter<User> userAdapter, TypeAdapter<ApiError> apiErrorAdapter) {
this.userAdapter = userAdapter;
this.apiErrorAdapter = apiErrorAdapter;
}
@Override
public void write(JsonWriter out, User value) throws IOException {
}
@Override
public User read(JsonReader in) throws IOException {
User user = null;
String type = null;
in.beginObject();
while (in.hasNext()) {
switch (in.nextName()) {
case "type":
type = in.nextString();
break;
case "data":
if ("error".equals(type)) {
throw apiErrorAdapter.read(in);
} else if ("success".equals(type)) {
user = userAdapter.read(in);
}
break;
}
}
in.endObject();
return user;
}
}
}
{
"data": {
"display_name": "Invisible Pink Unicorn",
"user_email": "user@example.com",
"user_id": 1234
},
"type": "success"
}
private static final Gson gson = new GsonBuilder()
.registerTypeAdapter(Content.class, getContentJsonDeserializer())
.create();
private static final Type userContent = new TypeToken<Content<User>>() {
}.getType();
public static void main(final String... args)
throws IOException {
for ( final String name : ImmutableList.of("success.json", "error.json", "success-reversed.json", "error-reversed.json") ) {
try ( final JsonReader jsonReader = getPackageResourceJsonReader(Q44400163.class, name) ) {
final Content<User> content = gson.fromJson(jsonReader, userContent);
if ( content.isSuccess() ) {
System.out.println("SUCCESS: " + content.getData().displayName);
} else {
System.out.println("ERROR: " + content.getError().errorMessage);
}
}
}
}