Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/202.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android JSON(Gson)反序列化到超级类对象然后强制转换到子类对象的最佳方式是什么_Android_Json_Serialization_Deserialization_Gson - Fatal编程技术网

Android JSON(Gson)反序列化到超级类对象然后强制转换到子类对象的最佳方式是什么

Android JSON(Gson)反序列化到超级类对象然后强制转换到子类对象的最佳方式是什么,android,json,serialization,deserialization,gson,Android,Json,Serialization,Deserialization,Gson,我目前正在学习在Android上使用Gson for Json,我刚刚遇到了这个问题。假设我们有如下类别: class Command { public int id = COMMAND_ID_UNSPECIFIED; } class CommandSpecific1 extends Command{ public String specialStr; public CommandSpecific1 () {

我目前正在学习在Android上使用Gson for Json,我刚刚遇到了这个问题。假设我们有如下类别:

    class Command {
        public int id = COMMAND_ID_UNSPECIFIED;
    }

    class CommandSpecific1 extends Command{
        public String specialStr;
        public CommandSpecific1 () {
            id = COMMAND_ID_SPECIAL1;
            specialStr= "special";
        }
    }
    class CommandSpecific2 extends Command{
        public int specialInt;
        public CommandSpecific2 () {
            id = COMMAND_ID_SPECIAL2;
            specialInt = 3.1415926;
        }
    }
我使用以下代码创建Json字符串

CommandSpecific specialCmd = new CommandSpecific();
Gson gson = new Gson();
String json = gson.toJson(specialCmd);
现在我想做一些类似这样的错误代码

Command genericCmd = gson.fromJson(json, Command.class)
if(genericCmd.id == COMMAND_ID_SPECIAL1) {
    CommandSpecific1 cmd1 = (CommandSpecific1)genericCmd;
    //do sth with cmd1.specialStr
} else if(genericCmd.id == COMMAND_ID_SPECIAL2) {
    CommandSpecific2 cmd2 = (CommandSpecific2)genericCmd;
    //do sth with cmd2.specialInt
}
代码不起作用,因为gson.fromJsonjson、Command.class仅为super类创建对象。 我知道我可以通过指定真实的类类型来调用fromJson,但是有没有更好的方法? 我应该使用定制的反序列化方法来解决这个问题吗?怎么做?

试试这个:

Command genericCmd = gson.fromJson(json, Command.class)
if(genericCmd.id == COMMAND_ID_SPECIAL1) {
    CommandSpecific1 cmd1 = gson.fromJson(json, CommandSpecific1.class);
    //do sth with cmd1.specialStr
} else if(genericCmd.id == COMMAND_ID_SPECIAL2) {
    CommandSpecific2 cmd2 = gson.fromJson(json, CommandSpecific2.class);
    //do sth with cmd2.specialInt
}
试试这个:

Command genericCmd = gson.fromJson(json, Command.class)
if(genericCmd.id == COMMAND_ID_SPECIAL1) {
    CommandSpecific1 cmd1 = gson.fromJson(json, CommandSpecific1.class);
    //do sth with cmd1.specialStr
} else if(genericCmd.id == COMMAND_ID_SPECIAL2) {
    CommandSpecific2 cmd2 = gson.fromJson(json, CommandSpecific2.class);
    //do sth with cmd2.specialInt
}
从文档:

此方法将指定的Json反序列化为 指定的类。如果指定的类是 泛型类型,因为它没有泛型类型信息 因为Java的类型擦除特性。因此,这种方法 如果所需类型是泛型类型,则不应使用。注意 如果指定的 对象是泛型,只是对象本身不应该是泛型 类型

因此,在使用fromJson方法时,必须使用确切的类

如果要创建通用反序列化方法,可以实现如下内容:

public Command deseralizeJson(String jsonString, Class targetClass) {
    return (Command)gson.fromJson(jsonString, targetClass);      
}
或更通用:

public Object deseralizeJson(String jsonString, Class targetClass) {
    return gson.fromJson(jsonString, targetClass);      
}
使用此方法时:

CommandSpecific1 cmd1 = (CommandSpecific1)deseralizeJson(json, CommandSpecific1.class);

编辑: 我现在明白你的主要问题了。在读取id的值之前,您不知道json响应的类型

您可以像现在这样继续反序列化两次。因为GSON在反序列化时需要精确的目标类 或者使用android的JSONObject。例如,

JSONObject jsonObject = new JSONObject(json);
// JsonString is converted to a JSONObject, it is much more efficient than gson serialization
int id = jsonObject.getInt("id");
if(id == COMMAND_ID_SPECIAL1) {
    CommandSpecific1 cmd1 = (CommandSpecific1)deseralizeJson(json, CommandSpecific1.class);
    //do sth with cmd1.specialStr
} else if(id == COMMAND_ID_SPECIAL2) {
    CommandSpecific2 cmd2 = (CommandSpecific2)deseralizeJson(json, CommandSpecific2.class);
    //do sth with cmd2.specialInt
}
从文档:

此方法将指定的Json反序列化为 指定的类。如果指定的类是 泛型类型,因为它没有泛型类型信息 因为Java的类型擦除特性。因此,这种方法 如果所需类型是泛型类型,则不应使用。注意 如果指定的 对象是泛型,只是对象本身不应该是泛型 类型

因此,在使用fromJson方法时,必须使用确切的类

如果要创建通用反序列化方法,可以实现如下内容:

public Command deseralizeJson(String jsonString, Class targetClass) {
    return (Command)gson.fromJson(jsonString, targetClass);      
}
或更通用:

public Object deseralizeJson(String jsonString, Class targetClass) {
    return gson.fromJson(jsonString, targetClass);      
}
使用此方法时:

CommandSpecific1 cmd1 = (CommandSpecific1)deseralizeJson(json, CommandSpecific1.class);

编辑: 我现在明白你的主要问题了。在读取id的值之前,您不知道json响应的类型

您可以像现在这样继续反序列化两次。因为GSON在反序列化时需要精确的目标类 或者使用android的JSONObject。例如,

JSONObject jsonObject = new JSONObject(json);
// JsonString is converted to a JSONObject, it is much more efficient than gson serialization
int id = jsonObject.getInt("id");
if(id == COMMAND_ID_SPECIAL1) {
    CommandSpecific1 cmd1 = (CommandSpecific1)deseralizeJson(json, CommandSpecific1.class);
    //do sth with cmd1.specialStr
} else if(id == COMMAND_ID_SPECIAL2) {
    CommandSpecific2 cmd2 = (CommandSpecific2)deseralizeJson(json, CommandSpecific2.class);
    //do sth with cmd2.specialInt
}

我会尝试像这样反序列化准备运行的示例。作为注释,您不希望使用id字段在命令之间切换,因此您必须信任字段结构,并假设每个子类都存在一组字段,这些字段唯一地标识您的子类

package stackoverflow.questions.q20185625;

import java.lang.reflect.Type;

import com.google.gson.*;

public class Q20185625 {

   public static class Command {
      public int id = -1;
   }

   public static class CommandSpecific1 extends Command {
      public String specialStr;

      public CommandSpecific1() {
         id = 1;
         specialStr = "special";
      }
   }

   public static class CommandSpecific2 extends Command {
      public int specialInt;

      public CommandSpecific2() {
         id = 2;
         specialInt = 42;
      }
   }

   public static class CustomDeserializer implements JsonDeserializer<Command> {

      public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

         if (json == null)
            return null;
         else {
            JsonElement e = json.getAsJsonObject().get("specialStr");
            if (e != null && e.isJsonPrimitive() && e.getAsString() instanceof String) {
               CommandSpecific1 c = new CommandSpecific1();
               c.specialStr = e.getAsString(); // do you need this?
               return c;
            }

            e = json.getAsJsonObject().get("specialInt");
            if (e != null && e.isJsonPrimitive() && e.getAsNumber() instanceof Number) {
               CommandSpecific2 c = new CommandSpecific2();
               c.specialInt = e.getAsInt(); // do you need this?
               return c;
            }
            return null; // or throw an IllegalArgumentException

         }

      }

   }

   public static void main(String[] args) {
      GsonBuilder gb = new GsonBuilder();
      gb.registerTypeAdapter(Command.class, new CustomDeserializer());
      Gson customGson = gb.create();

      String jsonTest1 = "{\"specialStr\":\"AA\"}";
      String jsonTest2 = "{\"specialInt\":13}";
      String jsonTest3 = "{}";
      String jsonTest4 = "";

      System.out.println("Deserialize test 1: " + customGson.fromJson(jsonTest1, Command.class));
      System.out.println("Deserialize test 2: " + customGson.fromJson(jsonTest2, Command.class));
      System.out.println("Deserialize test 3: " + customGson.fromJson(jsonTest3, Command.class));
      System.out.println("Deserialize test 4: " + customGson.fromJson(jsonTest4, Command.class));

   }

}
编辑 如果您的JSON也包含id字段,则更简单。您始终可以使用TypeAdapter,但方法很简单:

public static class CustomDeserializer implements JsonDeserializer<Command> {

      public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

         if (json == null)
            return null;
         else {
            // null management can be improved
            int id = json.getAsJsonObject().get("id").getAsInt();
            switch(id){
               case COMMAND_TYPE_1:
                  return context.deserialize(json, CommandSpecific1.class);
               case COMMAND_TYPE_2:
                  return context.deserialize(json, CommandSpecific2.class);
               default:
                  return null; 
            }
         }

      }

   }

但是,如果您对JSON的其余部分感兴趣,并且担心性能,但这不是最初的问题,您询问了一个子类化问题,您可以尝试使用TypeAdapter。

我会尝试像这个准备运行的示例那样反序列化。作为注释,您不希望使用id字段在命令之间切换,因此您必须信任字段结构,并假设每个子类都存在一组字段,这些字段唯一地标识您的子类

package stackoverflow.questions.q20185625;

import java.lang.reflect.Type;

import com.google.gson.*;

public class Q20185625 {

   public static class Command {
      public int id = -1;
   }

   public static class CommandSpecific1 extends Command {
      public String specialStr;

      public CommandSpecific1() {
         id = 1;
         specialStr = "special";
      }
   }

   public static class CommandSpecific2 extends Command {
      public int specialInt;

      public CommandSpecific2() {
         id = 2;
         specialInt = 42;
      }
   }

   public static class CustomDeserializer implements JsonDeserializer<Command> {

      public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

         if (json == null)
            return null;
         else {
            JsonElement e = json.getAsJsonObject().get("specialStr");
            if (e != null && e.isJsonPrimitive() && e.getAsString() instanceof String) {
               CommandSpecific1 c = new CommandSpecific1();
               c.specialStr = e.getAsString(); // do you need this?
               return c;
            }

            e = json.getAsJsonObject().get("specialInt");
            if (e != null && e.isJsonPrimitive() && e.getAsNumber() instanceof Number) {
               CommandSpecific2 c = new CommandSpecific2();
               c.specialInt = e.getAsInt(); // do you need this?
               return c;
            }
            return null; // or throw an IllegalArgumentException

         }

      }

   }

   public static void main(String[] args) {
      GsonBuilder gb = new GsonBuilder();
      gb.registerTypeAdapter(Command.class, new CustomDeserializer());
      Gson customGson = gb.create();

      String jsonTest1 = "{\"specialStr\":\"AA\"}";
      String jsonTest2 = "{\"specialInt\":13}";
      String jsonTest3 = "{}";
      String jsonTest4 = "";

      System.out.println("Deserialize test 1: " + customGson.fromJson(jsonTest1, Command.class));
      System.out.println("Deserialize test 2: " + customGson.fromJson(jsonTest2, Command.class));
      System.out.println("Deserialize test 3: " + customGson.fromJson(jsonTest3, Command.class));
      System.out.println("Deserialize test 4: " + customGson.fromJson(jsonTest4, Command.class));

   }

}
编辑 如果您的JSON也包含id字段,则更简单。您始终可以使用TypeAdapter,但方法很简单:

public static class CustomDeserializer implements JsonDeserializer<Command> {

      public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

         if (json == null)
            return null;
         else {
            // null management can be improved
            int id = json.getAsJsonObject().get("id").getAsInt();
            switch(id){
               case COMMAND_TYPE_1:
                  return context.deserialize(json, CommandSpecific1.class);
               case COMMAND_TYPE_2:
                  return context.deserialize(json, CommandSpecific2.class);
               default:
                  return null; 
            }
         }

      }

   }

但是,如果您对JSON的其余部分感兴趣,并且担心性能,但这不是最初的问题,您询问了一个子类问题,您可以尝试使用TypeAdapter。

这就是我目前所做的。但是除了再次调用fromJson方法,还有什么更好的方法吗?在这种情况下,不是首先将json转换为命令对象,使用字符串比较检查json字符串是否包含COMMAND_ID_SPECIAL1或COMMAND_ID_SPECIAL2,然后相应地将其转换为特定的COMMAND…COMMAND_ID_SPECIAL1和COMMAND_ID_SPECIAL2是整数。传递给fromJson方法的json是字符串,所以如果json.indexOfnew StringCOMMAND_ID_SPECIAL1>-1,请执行类似操作{CommandSpecific1 cmd1=gson.fromJsonjson,CommandSpecific1.class;}如果json.indexOfnew StringCOMMAND_ID_SPECIAL2>-1{CommandSpecific2 cmd2=gson.fromJsonjson,CommandSpecific2.class;}COMMAND_ID_SPECIAL1和COMMAND_ID_SPECIAL2是字段值,它是常量整数。它不会出现在json中。这是我目前所做的。但是除了再次调用fromJson方法之外,还有什么更好的方法吗?在这种情况下,不是首先将json转换为COMMAND对象,

使用字符串比较检查json字符串是否包含COMMAND_ID_SPECIAL1或COMMAND_ID_SPECIAL2,然后相应地将其转换为特定的COMMAND…COMMAND_ID_SPECIAL1和COMMAND_ID_SPECIAL2是整数。传递给fromJson方法的json是字符串,所以如果json.indexOfnew StringCOMMAND_ID_SPECIAL1>-1,请执行类似操作{CommandSpecific1 cmd1=gson.fromJsonjson,CommandSpecific1.class;}如果json.indexOfnew StringCOMMAND_ID_SPECIAL2>-1{CommandSpecific2 cmd2=gson.fromJsonjson,CommandSpecific2.class;}COMMAND_ID_SPECIAL1和COMMAND_ID_SPECIAL2是字段值,它是常量整数。它不会出现在json中。这仍然需要执行两次反序列化。我想要一些自定义方法,允许我只执行一次反序列化来查看ID字段并执行静态强制转换。反序列化两次?不,json反序列化到java对象一次。然后将其转换为相关类。要点是,gson在反序列化CommandSpecific1或CommandSpecific2时需要目标类。如果您传递了其中的一个超类,那么您只能反序列化超类的属性。根据我的情况,我需要执行genericCmd=gson.fromJsonjson,Command.class以获得查看ID字段,根据您的建议,我应该执行另一个cmd1=CommandSpecific1DeserializeJSONJSON,CommandSpecific1.class;来获取目标对象。两次,对吗?只有CommandSpecific1 cmd1=CommandSpecific1DeserializeJSONJSON,CommandSpecific1.class;就足够了。CommandSpecific1是Command的子类,所以它有属性ID。哦,我明白了。如果您如果只想继续使用gson,则必须反序列化twice,因为除非读取id值,否则您不知道目标类。您可以使用android的默认JSONObject类进行第一次解析。请参阅我的编辑。这仍然需要进行两次反序列化。我想要一些自定义方法,允许我只进行一次反序列化以查看id field并执行静态cast.deserialization两次?不,json反序列化为java对象一次。然后将其转换为相关类。要点是,gson在反序列化CommandSpecific1或CommandSpecific2时需要目标类。如果传递其中的一个超类,则只反序列化超类的属性。根据我的情况ion,我需要执行genericCmd=gson.fromJsonjson,Command.class以获得查看ID字段的机会,根据您的建议,我应该执行另一个cmd1=CommandSpecific1DeserializeJSONJSON,CommandSpecific1.class;以获取目标对象。两次,对吗?只有CommandSpecific1 cmd1=CommandSpecific1DeserializeJSONJSON,CommandSpecific1.class;够了。CommandSpecific1是Command的子类,因此它有属性id。哦,我明白了。如果你只想继续使用gson,你必须反序列化Twice1,因为你不知道目标类,除非你读取id值。你可以使用android的默认JSONObject类进行第一次解析。请参阅我的编辑。嗨,谢谢你发布了一些新的内容。你的swer与上面的不同。但是,我并没有说我不想切换ID字段。相反,我可以想象的最好的方法是这样做:步骤1。反序列化JSON只查看第一个ID字段。步骤2。反序列化JSON的左侧部分以获得正确的对象。这与步骤1类似,我们只是使用它第一个4字节的ID整数,在第二步中,我们使用了左边的字节。我认为这对于大型类来说很有意义。嗨,谢谢你发布了一些新的东西。你的答案与上面的答案真的不同。但是,我没有说我不想切换ID字段。相反,我能想到的最好方法是这样做:第1步。反序列化JSON仅用于查看第一个ID字段。STEP2.反序列化JSON的左侧部分以获得正确的对象。这类似于在STEP1中,我们只使用前4个字节的ID整数,而在STEP2中,我们使用左侧字节。我认为这对于大型类来说非常有意义。