JSON 解析器:解析和提取 JSON 字符串中的数据

· 12 分钟阅读

目录

理解 JSON 解析

JSON 解析器是一种专门的工具,用于解释 JSON(JavaScript 对象表示法)数据,将其从纯文本字符串转换为编程语言可以操作的结构化数据格式。这种转换是现代 Web 开发的基础,因为 JSON 已成为客户端和服务器之间数据交换的事实标准。

JSON 的流行源于其简洁性和人类可读性。与需要冗长的开始和结束标签的 XML 不同,JSON 使用简洁的语法,包括花括号、方括号和键值对。谷歌、亚马逊、Facebook 和 Twitter 等主要科技公司都依赖 JSON 作为其 API,每天处理数十亿个 JSON 请求。

当您从 REST API 获取数据、提交表单或加载配置文件时,您很可能正在使用 JSON。解析器充当翻译器,将序列化的字符串格式转换为本地数据结构,如对象、数组、数字和布尔值,您的代码可以直接访问和修改这些数据。

专业提示:在生产环境中解析 JSON 之前,始终先使用 JSON 格式化器和验证器 对其进行验证,以便及早发现语法错误并避免运行时异常。

为什么 JSON 解析很重要

理解 JSON 解析至关重要,原因如下:

JSON 解析器的工作原理

JSON 解析器通过多阶段过程运行,将字符串分解为标记,验证结构,并构建相应的数据对象。理解这个过程可以帮助您编写更高效的代码并有效地调试解析问题。

解析流程

典型的 JSON 解析工作流程包括四个主要阶段:

  1. 词法分析(标记化):解析器逐字符扫描输入字符串,识别标记,如花括号、方括号、冒号、逗号、字符串、数字和关键字(true、false、null)
  2. 语法分析:根据 JSON 语法规则检查标记以确保结构正确。解析器验证花括号是否匹配,逗号是否正确分隔元素,以及键是否始终为字符串
  3. 语义分析:解析器验证 JSON 结构在逻辑上是否合理,检查重复键和正确的嵌套
  4. 对象构造:最后,解析器在您的编程语言中构建本地数据结构,将 JSON 对象映射到字典/对象,将 JSON 数组映射到列表/数组

基本解析示例

以下是一个简单示例,展示 JSON 解析如何将字符串转换为可用数据:

// 从 API 接收的 JSON 字符串
const jsonString = '{"name":"Alice","age":30,"skills":["JavaScript","Python","Go"],"isDeveloper":true}';

// 将字符串解析为 JavaScript 对象
const userData = JSON.parse(jsonString);

// 现在您可以直接访问数据
console.log(userData.name);        // 输出:Alice
console.log(userData.skills[0]);   // 输出:JavaScript
console.log(userData.isDeveloper); // 输出:true

解析器将扁平字符串转换为结构化对象,您可以使用点表示法或括号表示法访问属性。这使得数据操作变得简单直观。

理解 JSON 数据类型

JSON 支持解析器必须识别和转换的六种基本数据类型:

JSON 类型 描述 示例 JavaScript 等效类型
字符串 用双引号括起来的文本 "hello" String
数字 整数或浮点数 423.14 Number
布尔值 真或假值 truefalse Boolean
空值 表示值的缺失 null null
对象 键值对的集合 {"key":"value"} Object
数组 有序的值列表 [1,2,3] Array

手动解析与使用库

在处理 JSON 时,您有两种主要方法:从头开始编写自己的解析器或使用成熟的库。每种方法都有明显的优势和权衡,具体取决于您的特定用例。

使用内置库(推荐)

大多数现代编程语言都包含本地 JSON 解析功能。这些内置解析器经过实战测试、优化,并处理您在构建自己的解析器时可能不会考虑的边缘情况。

基于库的解析的优势:

何时使用库:

手动解析实现

手动构建 JSON 解析器是一个很好的学习练习,可以加深您对解析算法、状态机和语言设计的理解。但是,它很少适合生产使用。

何时手动解析有意义:

以下是基本对象的手动 JSON 解析的简化示例:

function simpleJSONParse(jsonString) {
  let index = 0;
  
  function parseValue() {
    skipWhitespace();
    const char = jsonString[index];
    
    if (char === '{') return parseObject();
    if (char === '[') return parseArray();
    if (char === '"') return parseString();
    if (char === 't' || char === 'f') return parseBoolean();
    if (char === 'n') return parseNull();
    if (char === '-' || (char >= '0' && char <= '9')) return parseNumber();
    
    throw new Error(`Unexpected character: ${char}`);
  }
  
  function parseObject() {
    const obj = {};
    index++; // 跳过左花括号
    skipWhitespace();
    
    while (jsonString[index] !== '}') {
      const key = parseString();
      skipWhitespace();
      index++; // 跳过冒号
      const value = parseValue();
      obj[key] = value;
      skipWhitespace();
      if (jsonString[index] === ',') index++;
      skipWhitespace();
    }
    
    index++; // 跳过右花括号
    return obj;
  }
  
  // 其他解析函数将放在这里...
  
  return parseValue();
}

快速提示:如果您正在构建手动解析器进行学习,请针对 json.org/JSON_checker 上的官方 JSON 测试套件进行测试,以确保它正确处理所有有效和无效情况。

在不同编程语言中解析 JSON

每种主要编程语言都提供 JSON 解析功能,尽管语法和方法各不相同。理解这些差异可以帮助您在不同的技术栈中有效工作。

JavaScript/Node.js

JavaScript 通过全局 JSON 对象直接在语言中内置了本地 JSON 支持:

// 将 JSON 字符串解析为对象
const data = JSON.parse('{"name":"Bob","age":25}');

// 将对象转换为 JSON 字符串
const jsonString = JSON.stringify(data);

// 使用缩进进行美化打印
const formatted = JSON.stringify(data, null, 2);

Python

Python 的 json 模块提供了具有直观方法名称的全面 JSON 处理:

import json

# 解析 JSON 字符串
json_string = '{"name":"Bob","age":25}'
data = json.loads(json_string)

# 从文件解析 JSON
with open('data.json', 'r') as file:
    data = json.load(file)

# 转换为 JSON 字符串
json_output = json.dumps(data, indent=2)

Java

Java 需要像 Jackson 或 Gson 这样的外部库来进行 JSON 解析:

// 使用 Jackson
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"name\":\"Bob\",\"age\":25}";
User user = mapper.readValue(jsonString, User.class);

// 使用 Gson
Gson gson = new Gson();
User user = gson.fromJson(jsonString, User.class);

Go

Go 的 encoding/json 包使用结构标签进行映射:

import "encoding/json"

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

// 解析 JSON
var user User
json.Unmarshal([]byte(jsonString), &user)

// 创建 JSON
jsonBytes, _ := json.Marshal(user)

语言比较表

语言 解析方法 字符串化方法 需要库 类型安全
JavaScript JSON.parse() JSON.stringify() 否(内置) 动态
Python json.loads() json.dumps() 否(标准库) 动态
Java readValue() writeValue() 是(Jackson/Gson) 静态
Go Unmarshal() Marshal() 否(标准库) 静态
C# JsonSerializer.Deserialize() JsonSerializer.Serialize() 否(.NET Core 3.0+) 静态

高级 JSON 解析技术

除了基本解析之外,几种高级技术可以帮助您处理复杂场景,如深度嵌套数据、大文件和动态模式。

流式 JSON 解析

在处理大型 JSON 文件(数百兆字节或千兆字节)时,将整个文件加载到内存中是不切实际的。流式解析器逐步处理 JSON,一次读取一个块。

// Node.js 流式示例
const fs = require('fs');
const JSONStream = require('JSONStream');

fs.createReadStream('large-file.json')
  .pipe(JSONStream.parse('items.*'))
  .on('data', (item) => {
    // 单独处理每个项目
    console.log(item);
  });

流式传输特别适用于:

用于复杂查询的 JSONPath

JSONPath 提供类似 XPath 的语法来查询 JSON 结构,使从复杂嵌套对象中提取特定数据变得容易:

const jp = require('jsonpath');

const data = {
  store: {
    books: [
      { title: "Book 1", price: 10 },
      { title: "Book 2", price: 15 },
      { title: "Book 3", price: 20 }
    ]
  }
};

// 查找价格低于 18 的所有书籍
const affordableBooks = jp.query(data, '$.store.books[?(@.price < 18)]');
// 结果:[{ title: "Book 1", price: 10 }, { title: "Book 2", price: 15 }]

模式验证

JSON Schema 允许您定义 JSON 数据的预期结构,并根据它验证传入的负载:

const Ajv = require('ajv');
const ajv = new Ajv();

const schema = {
  type: "object",
  properties: {
    name: { type: "string" },
    age: { type: "number", minimum: 0 }
  },
  required: ["name", "age"]
};

const validate = ajv.compile(schema);
const valid = validate({ name: "Alice", age: 30 });

if (!valid) {
  console.log(validate.errors);
}