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 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 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 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数组对象,它们的属性都映射正确:


Gson教程三(译):Arrays和Lists的映射_第1张图片

Lists

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

Type founderListType = new TypeToken>(){}.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>(){}.getType();

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

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

Gson教程三(译):Arrays和Lists的映射_第2张图片

最后,映射你的数据到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 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); 

产生的结果是完美的:

Gson教程三(译):Arrays和Lists的映射_第3张图片

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

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

嵌套在列表里面的列表

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

public class GeneralInfo {  
    String name;
    String website;
    List founders;
}

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

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

Pet类又拥有玩具的列表

public class Pet {  
    String name;
    List toys;
}

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

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

你可能感兴趣的:(Gson教程三(译):Arrays和Lists的映射)