Gson教程三(译):Arrays和Lists的映射

该文章翻译自Gson Tutorial Series系列教程。该篇主要阐述了如何使用Gson映射Arrays和List集合对象。

Arrays和Lists之间的不同

在进入正题之前,我们想阐述一下Arrays和Lists这两种Java数据结构。他们的Java实现是不同的并且各有各的优势。在你的用例中采取哪种方式取决于软件需求以及你个人的喜好。有趣的是,什么是选择list还是array结构映射到JSON是无关紧要的。

在JSON的数据格式中,没有lists和arrays。是的,Java的实现造成了二者之间巨大的区别,但在就高层次来说,他们都是代表相同的列表结构。在接下来的博客中,我们将他们成为对象列表,但是在Java中他们又是不同的。如果你对此感到迷惑,不用担心,一些例子将会使你更加清晰。

Arrays或者Lists数据的序列化

还记得前一篇博客中关于嵌套对象的restaurant模型吗?现在是时候为它增加一个菜单了。我们想要知道美味食物的价格。一个饭店的菜单可以看成是菜单项的列表结构。每一项都是可供顾客点单的。在我们的简易饭店中,每一项都包括描述和价格。

提示:我们没有考虑菜品分类,组合套餐以及配菜,这使我们的例子足够简单。这很明显不是一个完美的模型,因此不要在你的商业饭店App中使用它……

如果我们考虑怎样具体实现其Java模型,我们将得到接近如下的代码:

public class RestaurantWithMenu {  
    String name;

    List<RestaurantMenuItem> menu;
    //RestaurantMenuItem[] menu; // alternative, either one is fine
}

public class RestaurantMenuItem {  
    String description;
    float price;
}

Java处理嵌套对象的方式和JSON是不同的。Java可以将之分离到不同的类中,并由List或者Array的来持有其引用。JSON需要保持一个本地的、嵌套的列表。这意味着在高层次,我们希望JSON像下面这样:

{
  "name": "Future Studio Steak House",
  "menu": [
    ...
  ]
}

就像嵌套对象一样,menu并不拥有一个直接的值。相反,JSON为其值定义了一个由[]包裹着的对象列表。如上面提到的,这是array还是list是没有区别的。在JSON的数据结构中它看起来是相同的。

menu由很多对象组成。在我们的例子中,它们是饭店菜单项。让我们运行Gson查看一个完整的JSON会是什么样子。

我们希望你现在已经知道常规步骤了。得到你的Java对象,初始化Gson然后让Gson创建相应的JSON:

List<RestaurantMenuItem> menu = new ArrayList<>();  
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));  
menu.add(new RestaurantMenuItem("Steak", 12.99f));  
menu.add(new RestaurantMenuItem("Salad", 5.99f));

RestaurantWithMenu restaurant =  
        new RestaurantWithMenu("Future Studio Steak House", menu);

Gson gson = new Gson();  
String restaurantJson = gson.toJson(restaurant);

restaurantJson包含如下内容:

{
  "menu": [
    {
      "description": "Spaghetti",
      "price": 7.99
    },
    {
      "description": "Steak",
      "price": 12.99
    },
    {
      "description": "Salad",
      "price": 5.99
    }
  ],
  "name": "Future Studio Steak House"
}

就跟通常一样,排序有点神奇,列表排在前面是因为按字母来说menu在name的前面。除了排序意外,其他一切都是我们希望的。列表(由[]包裹)包含多个对象(每一个对象由{}包裹)。

然而,我们并不总是发送一个嵌套了列表数据的单独对象,就像上面做的那样。有时候我们也希望发送一个列表。当然,Gson同样支持JSON列表的序列化。例如,如果我们希望序列化如下菜单项列表:

List<RestaurantMenuItem> menu = new ArrayList<>();  
menu.add(new RestaurantMenuItem("Spaghetti", 7.99f));  
menu.add(new RestaurantMenuItem("Steak", 12.99f));  
menu.add(new RestaurantMenuItem("Salad", 5.99f));

Gson gson = new Gson();  
String menuJson = gson.toJson(menu);  

将会得到如下结果:

[
  {
    "description": "Spaghetti",
    "price": 7.99
  },
  {
    "description": "Steak",
    "price": 12.99
  },
  {
    "description": "Salad",
    "price": 5.99
  }
]

让我们指出重要的不同之处:该JSON的首个字符为[,这就提示了接下来是对象列表!到目前为止,我们只看到由{开始的对象。你应该马上记住它们之间的差异。你将总是需要它并在下一章节马上回用到,如果你继续阅读的话。

Arrays或者Lists的序列化和反序列化

在第二部分我们学习反序列化。换句话说,我们如何使用Gson映射JSON结构中的列表到Java对象。在之前的例子中,我们看到了JSON数据中作为根列表和嵌套在对象中的列表的重要不同。

列表作为根对象

让我们做一个练习。考虑这样一种情况,在未来的环境中我们将会开启自己的API,并会提供一个端口GET /founders。该端口将返回三个对象的数组,每个对象包含一个name域和一个flowerCount域。该数组代表我们桌子上的植物。因此如下JSON列表:

[
    {
      "name": "Christian",
      "flowerCount": 1
    },
    {
      "name": "Marcus",
      "flowerCount": 3
    },
    {
      "name": "Norman",
      "flowerCount": 2
    }
]

是的,你是对的。这个想象的端口直接返回了一个列表。JSON以[]开始和结束。没错,Marcus也喜欢在绿植环绕的办公桌上工作。

因此,我们如何使用Gson将此映射到Java对象呢?第一步是创建模型:

public class Founder {  
    String name;
    int flowerCount;
}

第二部取决于你。你是希望使用Lists还是Arrays作为你的类型呢?

Arrays

如果你想要使用Arrays,那太简单了。跟之前一样直接调用fromJson()方法并且传递数组模型的class,就像:gson.fromJson(founderGson, Founder[].class);

String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";

Gson gson = new Gson();  
Founder[] founderArray = gson.fromJson(founderJson, Founder[].class); 

这将会产生一个founder的Java数组对象,它们的属性都映射正确:


Lists

鉴于Lists可以扩展容量,因此现在的开发者更喜欢使用Java Lists。不幸的是,你不能直接传递List<Founder>给Gson。为了使Gson知道List的准确结构,你需要得到它的Type。幸运的是,Gson有一个TypeToken类帮助你正确找到任何类的Type。我们的Founder类在一个ArrayList中,让我们看一下:

Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType(); 

你可以使用该语句的结果作为type供Gson调用:

String founderJson = "[{'name': 'Christian','flowerCount': 1}, {'name': 'Marcus', 'flowerCount': 3}, {'name': 'Norman', 'flowerCount': 2}]";

Gson gson = new Gson();

Type founderListType = new TypeToken<ArrayList<Founder>>(){}.getType();

List<Founder> founderList = gson.fromJson(founderJson, founderListType);  

结果跟使用Array得到的结果差不多:


最后,映射你的数据到Array还是List取决于你的个人偏好和用例情况。让我们探讨另一个主题:怎样反序列化一个嵌套在一个对象中的列表:

列表作为一个对象的一部分

我们已经扩展了我们想象的未来环境中的API,端口为GET /info。它返回了比founder更多的信息:

{
  "name": "Future Studio Dev Team",
  "website": "https://futurestud.io",
  "founders": [
    {
      "name": "Christian",
      "flowerCount": 1
    },
    {
      "name": "Marcus",
      "flowerCount": 3
    },
    {
      "name": "Norman",
      "flowerCount": 2
    }
  ]
}

我们希望你已经熟悉流程了。首先我们需要写一个与JSON响应匹配的模型。我们可以复用之前的Founder类:

public class GeneralInfo {  
    String name;
    String website;
    List<Founder> founders;
}

处理嵌套在一个对象中的列表是简单的,因为Gson不用使用TypeToken就可以简单地处理了。我们可以直接传入class:

String generalInfoJson = "{'name': 'Future Studio Dev Team', 'website': 'https://futurestud.io', 'founders': [{'name': 'Christian', 'flowerCount': 1 }, {'name': 'Marcus','flowerCount': 3 }, {'name': 'Norman','flowerCount': 2 }]}";

Gson gson = new Gson();

GeneralInfo generalInfoObject = gson.fromJson(generalInfoJson, GeneralInfo.class); 

产生的结果是完美的:


当然,你也可以使用Founder[]数组代替List<Founder>。Gson同样可以处理。

注意:你注意到没有,GeneralInfo和Founder模型拥有相同的name属性,但是Gson却没有出现问题?在序列化和反序列化过程中不会出现任何问题。这真是太神奇了。

嵌套在列表里面的列表

如果你正好奇,在处理嵌套在列表里面的列表时会不会有问题。例如,下面的模型将不会有问题:

public class GeneralInfo {  
    String name;
    String website;
    List<FounderWithPets> founders;
}

FounderWithPets类是由宠物列表扩展而来的:

public class FounderWithPets {  
    String name;
    int flowerCount;
    List<Pet> pets;
}

Pet类又拥有玩具的列表

public class Pet {  
    String name;
    List<Toy> toys;
}

Toy类又包含……好了,循环停止吧。我们希望这个推论可以带给你这样一个观点:你可以在列表中逐层的包含列表而不会有任何问题。Gson就像个冠军一样良好的处理序列化和反序列化。

不过,Gson只能处理包含一致性的对象的列表。如果对象是完全不同的,Gson就不能映射了。尽管可以由多种类型组成列表。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容