1. 概述

JSON(JavaScript Object Notation)是现代API和数据服务中广泛使用的结构化数据格式。因其轻量级特性和与JavaScript的兼容性,在Web应用中尤其流行。

有时,在展示从API获取的JSON数据前,对其进行排序会很有用。

本教程将介绍几种在Java中对JSON对象进行排序的方法。

2. 准备工作

首先定义一个相对简单的JSON结构,用于模拟一些太阳活动事件:

{
    "solar_events": [
        {
            "event_name": "Solar Eclipse",
            "date": "2024-04-08",
            "coordinates": {
                "latitude": 37.7749,
                "longitude": -122.4194
            },
            "size": "Large",
            "speed_km_per_s": 1000
        },
        {
            "event_name": "Solar Flare",
            "date": "2023-10-28",
            "coordinates": {
                "latitude": 0,
                "longitude": 0
            },
            "size": "Small",
            "speed_km_per_s": 100
        },
        {
            "event_name": "Sunspot",
            "date": "2023-11-15",
            "coordinates": {
                "latitude": 15,
                "longitude": -75
            },
            "size": "Large",
            "speed_km_per_s": 1500
        },
        {
            "event_name": "Coronal Mass Ejection",
            "date": "2024-01-10",
            "coordinates": {
                "latitude": -5,
                "longitude": 80
            },
            "size": "Medium",
            "speed_km_per_s": 500
        }
    ]
}

本教程的所有示例都将基于这个JSON文档。

3. 使用Jackson

首先介绍Jackson,这是一个多功能的Java JSON处理库。

3.1 添加依赖

pom.xml中添加jackson-databind依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.2</version>
</dependency>

3.2 定义POJO

定义几个简单的POJO类用于反序列化:

class SolarEvent {
    @JsonProperty("event_name")
    private String eventName;
    @JsonProperty("date")
    private String date;
    @JsonProperty("coordinates")
    private Coordinates coordinates;
    @JsonProperty("type")
    private String type;
    @JsonProperty("class")
    private String eventClass;
    @JsonProperty("size")
    private String size;
    @JsonProperty("speed_km_per_s")
    private int speedKmPerS;

    // Getters and Setters
}

class Coordinates {
    @JsonProperty("latitude")
    private double latitude;

    @JsonProperty("longitude")
    private double longitude;

    // Getters and setters
}

定义容器类持有事件列表:

class SolarEventContainer {
    @JsonProperty("solar_events")
    private List<SolarEvent> solarEvents;

    //Getters and setters
}

3.3 排序实现

编写单元测试验证按速度排序:

@Test
void givenJsonObjects_whenUsingJackson_thenSortedBySpeedCorrectly() throws IOException {
    ObjectMapper objectMapper = new ObjectMapper();
    SolarEventContainer container =
      objectMapper.readValue(new File("src/test/resources/solar_events.json"),
      SolarEventContainer.class);

    List<SolarEvent> events = container.getSolarEvents();
    Collections.sort(events, Comparator.comparingInt(event -> event.getSpeedKmPerS()));

    assertEquals(100, events.get(0).getSpeedKmPerS());
    assertEquals(500, events.get(1).getSpeedKmPerS());
    assertEquals(1000, events.get(2).getSpeedKmPerS());
    assertEquals(1500, events.get(3).getSpeedKmPerS());
}

关键点解析:

  1. 使用ObjectMapper解析JSON文件为Java对象
  2. 通过Comparator.comparingInt()创建基于速度的比较器
  3. 使用Collections.sort()执行排序
  4. ✅ 验证排序结果正确

优势:类型安全,代码简洁,IDE支持完善
踩坑点:需要预先定义POJO类,结构变化时需同步更新

4. 使用Gson

现在看看使用Gson库的替代方案。

4.1 添加依赖

pom.xml中添加Gson依赖:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.11.0</version>
</dependency>

4.2 直接操作JSON对象

当无法定义POJO类时,可直接操作Gson的JSON对象:

@Test
public void givenJsonObject_whenUsingGson_thenSortedBySizeCorrectly() throws FileNotFoundException {
    JsonReader reader = new JsonReader(new FileReader("src/test/resources/solar_events.json"));
    JsonElement element = JsonParser.parseReader(reader);
    JsonArray events = element.getAsJsonObject().getAsJsonArray("solar_events");
    List<JsonElement> list = events.asList();

    Collections.sort(list, (a, b) -> {
        double latA = a.getAsJsonObject()
          .getAsJsonObject("coordinates")
          .get("latitude")
          .getAsDouble();
        double latB = b.getAsJsonObject()
          .getAsJsonObject("coordinates")
          .get("latitude")
          .getAsDouble();
        return Double.compare(latA, latB);
    });

    assertEquals(-5, getJsonAttributeAsInt(list.get(0)));
    assertEquals(0, getJsonAttributeAsInt(list.get(1)));
    assertEquals(15, getJsonAttributeAsInt(list.get(2)));
    assertEquals(37, getJsonAttributeAsInt(list.get(3)));
}

private int getJsonAttributeAsInt(JsonElement element) {
    return element.getAsJsonObject()
      .getAsJsonObject("coordinates")
      .get("latitude")
      .getAsInt();
}

关键点解析:

  1. 使用JsonParser解析JSON为JsonElement
  2. 通过链式调用获取嵌套属性值
  3. 自定义比较器按纬度排序
  4. ⚠️ 需要辅助方法提取属性值

优势:无需定义POJO,适合动态结构
踩坑点

  • ❌ 代码冗长易错
  • ❌ 属性名硬编码,结构变化时易崩溃
  • ❌ 缺乏类型安全

5. 总结

本文介绍了两种在Java中排序JSON对象的方法:

方案对比

特性 Jackson方案 Gson方案
类型安全 ✅ 强类型 ❌ 运行时解析
代码简洁性 ✅ 简洁明了 ❌ 冗长复杂
维护成本 ✅ 结构变化时易修改 ❌ 需全局检查属性名
适用场景 已知结构的业务逻辑 动态/临时数据处理

选择建议

  1. 优先使用Jackson方案

    • 适用于大多数业务场景
    • 开发效率高,维护成本低
    • 编译期即可发现问题
  2. 谨慎使用Gson方案

    • 仅适用于无法预知JSON结构的场景
    • 需要完善的异常处理
    • 建议封装通用工具类减少重复代码

最终建议:在实际项目中,除非有特殊需求,否则强烈推荐使用Jackson的POJO方案。虽然需要额外定义类,但带来的类型安全和代码可维护性提升远超这点代价。

完整示例代码可在GitHub仓库获取。


原始标题:Sort JSON Objects in Java | Baeldung