App Inventor 2 JSON数据解析教程 - 解析API返回的JSON数据

« 返回首页

什么是JSON

JSON(JavaScript Object Notation,JavaScript对象表示法)是一种轻量级的数据交换格式,广泛用于Web API的数据传输。几乎所有现代API接口都使用JSON格式返回数据。

一个典型的JSON数据示例:

{
  "name": "Tim Beaver",
  "age": 25,
  "enrolled": true,
  "school": {
    "name": "MIT",
    "city": "Cambridge"
  },
  "courses": ["Math", "Physics", "CS"]
}

JSON中的数据类型

JSON类型 示例 App Inventor对应类型
字符串(String) "hello" 文本(text)
数字(Number) 42, 3.14 数字(number)
布尔值(Boolean) true, false 布尔值(boolean)
数组(Array) [1, 2, 3] 列表(list)
对象(Object) {"key": "value"} 字典(dictionary)

核心要点: JSON对象({})在App Inventor中对应字典(dictionary),JSON数组([])对应列表(list)

核心工具

在App Inventor 2中解析JSON,需要用到以下两个核心工具:

1. Web组件

Web组件用于发送HTTP请求获取API数据。它位于「连接」类别中。

关键方法:

  • Get — 发送HTTP GET请求
  • GotText事件 — 请求完成时触发,参数包括 responseCode(状态码)和 responseContent(响应内容)

2. 字典积木块

字典积木块位于「字典」类别中,是解析JSON的核心工具。

关键积木块:

  • get value for key — 通过键名获取字典中的值
  • get value at key path — 通过键路径获取嵌套数据
  • is a dictionary? — 判断是否为字典类型
  • list by walking key path — 批量提取嵌套数据

3. JsonTextDecodeWithDictionaries方法

这是Web组件提供的方法,专门用于将JSON文本转换为App Inventor的字典/列表结构

⚠️ 注意:请使用 JsonTextDecodeWithDictionaries 而非 JsonTextDecode。后者将JSON对象转换为列表对(旧版行为),前者转换为字典(推荐行为)。

解析简单JSON

假设API返回如下简单JSON:

{
  "userId": 1,
  "id": 1,
  "title": "Hello World",
  "body": "This is a test post."
}

步骤

  1. 使用Web组件发送GET请求
  2. 在GotText事件中,用 JsonTextDecodeWithDictionaries 解析响应内容
  3. get value for key 提取具体字段

积木块伪代码

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  如果 responseCode = 200 则
    设 变量 jsonData 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
    设 变量 title 为 get value for key: "title" 从字典: jsonData
    设 变量 body 为 get value for key: "body" 从字典: jsonData
    设置 Label1.文本 为 title
    设置 Label2.文本 为 body
  否则
    设置 Label1.文本 为 "请求失败,状态码:" + responseCode

要点说明

  • responseCode = 200 表示请求成功
  • JsonTextDecodeWithDictionaries 返回一个字典对象
  • get value for key 通过键名(如 "title")获取对应的值
  • 如果键不存在,get value for key 返回 not found 参数的默认值

解析嵌套JSON

实际API返回的数据往往是多层嵌套的。例如:

{
  "id": 1,
  "name": "Tim the Beaver",
  "school": {
    "name": "Massachusetts Institute of Technology",
    "city": "Cambridge"
  },
  "enrolled": true,
  "classes": ["6.001", "18.01", "8.01"]
}

这里 school 的值又是一个JSON对象(字典),classes 的值是一个JSON数组(列表)。

方法一:逐层获取

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  如果 responseCode = 200 则
    设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
    
    // 第一层:获取name
    设 变量 name 为 get value for key: "name" 从字典: data
    
    // 第二层:先获取school字典,再从中获取name
    设 变量 school 为 get value for key: "school" 从字典: data
    设 变量 schoolName 为 get value for key: "name" 从字典: school
    
    设置 Label1.文本 为 name
    设置 Label2.文本 为 schoolName

方法二:使用 get value at key path(推荐)

get value at key path 可以通过一个路径列表一次性深入嵌套结构,更加简洁:

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  如果 responseCode = 200 则
    设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
    
    // 使用键路径直接获取嵌套值
    设 变量 schoolName 为 get value at key path: 
      生成列表("school", "name") 从字典: data
    
    设置 Label1.文本 为 schoolName

路径 ["school", "name"] 的含义:先取 school 键的值(一个字典),再从该字典中取 name 键的值。

解析JSON数组

JSON数组在App Inventor中会被解析为列表。当JSON中包含数组时,可以用列表操作来处理。

示例数据

{
  "people": [
    {"first_name": "Tim", "last_name": "Beaver"},
    {"first_name": "John", "last_name": "Smith"},
    {"first_name": "Jane", "last_name": "Doe"}
  ]
}

方法一:使用 for each 循环

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  如果 responseCode = 200 则
    设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
    设 变量 people 为 get value for key: "people" 从字典: data
    设 变量 names 为 创建空列表
    
    for each item person in 列表 people
      设 变量 firstName 为 get value for key: "first_name" 从字典: person
      添加 items: firstName 到列表 names
    
    设置 Label1.文本 为 join with separator(", ", names)
    // 结果显示: Tim, John, Jane

方法二:使用 list by walking key path(高级技巧)

list by walking key path 配合 walk all at level 可以一步到位:

提取所有 first_name:

设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
设 变量 firstNames 为 list by walking key path:
  路径: 生成列表("people", walk all at level, "first_name")
  从: data
// firstNames = ["Tim", "John", "Jane"]

解释:

  1. "people" — 进入 people 键,得到一个列表
  2. walk all at level — 遍历列表中的每一个元素(每个字典)
  3. "first_name" — 从每个字典中取 first_name 键的值

混合字典和数组的路径

如果 JSON 数据为:

{
  "id": 1,
  "name": "Tim the Beaver",
  "classes": ["6.001", "18.01", "8.01"]
}

要获取第二门课程("18.01"),可以用数字索引:

设 变量 secondClass 为 get value at key path:
  生成列表("classes", 2) 从字典: data
// secondClass = "18.01"

注意:App Inventor的列表索引从 1 开始,不是从 0 开始。

实战案例:调用天气API获取天气数据

需求描述

调用一个免费的天气API,获取指定城市的天气信息并显示。

UI设计

组件 名称 用途
TextBox CityTextBox 输入城市名
Button QueryButton 查询按钮
Label TempLabel 显示温度
Label DescLabel 显示天气描述
Label HumidityLabel 显示湿度
Web Web1 网络请求组件

API说明

以 wttr.in 为例(免费天气API,无需API Key):

https://wttr.in/Beijing?format=j1

返回的JSON结构(简化版):

{
  "current_condition": [
    {
      "temp_C": "25",
      "weatherDesc": [{"value": "Sunny"}],
      "humidity": "45",
      "FeelsLikeC": "27"
    }
  ],
  "nearest_area": [
    {
      "areaName": [{"value": "Beijing"}],
      "country": [{"value": "China"}]
    }
  ]
}

完整积木块伪代码

按钮点击 — 发送请求:

当 QueryButton.被点击 时
  设 变量 city 为 CityTextBox.文本
  如果 city ≠ "" 则
    设置 Web1.Url 为 "https://wttr.in/" + city + "?format=j1"
    调用 Web1.Get()
  否则
    设置 TempLabel.文本 为 "请输入城市名"

接收响应 — 解析JSON:

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  如果 responseCode = 200 则
    
    // 第一步:将JSON文本解析为字典
    设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
    
    // 第二步:获取 current_condition 数组的第一个元素
    设 变量 conditions 为 get value for key: "current_condition" 从字典: data
    设 变量 current 为 select list item: 索引 1 从列表: conditions
    
    // 第三步:从当前天气字典中提取各字段
    设 变量 temp 为 get value for key: "temp_C" 从字典: current
    设 变量 humidity 为 get value for key: "humidity" 从字典: current
    设 变量 feelsLike 为 get value for key: "FeelsLikeC" 从字典: current
    
    // 第四步:获取天气描述(嵌套更深一层)
    设 变量 descList 为 get value for key: "weatherDesc" 从字典: current
    设 变量 descItem 为 select list item: 索引 1 从列表: descList
    设 变量 desc 为 get value for key: "value" 从字典: descItem
    
    // 第五步:显示结果
    设置 TempLabel.文本 为 "温度:" + temp + "°C(体感 " + feelsLike + "°C)"
    设置 DescLabel.文本 为 "天气:" + desc
    设置 HumidityLabel.文本 为 "湿度:" + humidity + "%"
    
  否则
    设置 TempLabel.文本 为 "请求失败: " + responseCode

使用 get value at key path 简化

上面的步骤二、三可以简化为:

// 直接用键路径获取温度
设 变量 temp 为 get value at key path: 
  生成列表("current_condition", 1, "temp_C") 
  从字典: data

// 直接用键路径获取湿度
设 变量 humidity 为 get value at key path:
  生成列表("current_condition", 1, "humidity")
  从字典: data

使用 list by walking key path 提取多个城市数据

如果返回了多个城市的数据(数组),可以批量提取:

设 变量 allTemps 为 list by walking key path:
  路径: 生成列表("current_condition", walk all at level, "temp_C")
  从: data
// 得到所有城市的温度列表

常用字典积木块参考

创建字典

make a dictionary:
  pair: "name" → "Alice"
  pair: "age" → 20

查询操作

积木块 功能 示例
get value for key 通过键获取值 get "name" → "Alice"
get value at key path 通过路径获取嵌套值 get ["school", "name"] → "MIT"
is key in dictionary? 判断键是否存在 "name" in dict? → true
get keys 获取所有键 → ["name", "age"]
get values 获取所有值 → ["Alice", 20]
size of dictionary 获取键值对数量 → 2

修改操作

积木块 功能
set value for key 设置键值(不存在则创建)
set value for key path 设置嵌套键值
delete entry for key 删除指定键

类型判断

积木块 功能
is a dictionary? 判断是否为字典
is a list? 判断是否为列表

常见错误与解决方法

1. 使用了 JsonTextDecode 而非 JsonTextDecodeWithDictionaries

错误现象: 解析JSON对象后得到的是列表 (("key1" "value1") ("key2" "value2")) 而非字典,无法使用 get value for key

解决方法: 始终使用 JsonTextDecodeWithDictionariesJsonTextDecode 是旧版方法,将JSON对象转换为列表对形式(与 lookup in pairs 配合使用),不推荐用于新项目。

2. 忘记检查 responseCode

错误现象: 直接解析 responseContent,但请求可能失败(404、500等),此时 responseContent 不是有效JSON。

解决方法: 始终先判断 responseCode = 200 再解析:

当 Web1.GotText(...) 时
  如果 responseCode = 200 则
    // 解析JSON
  否则
    // 提示错误

3. 键名拼写错误

错误现象: get value for key 返回 “not found”,但JSON中确实有数据。

解决方法: 仔细核对键名,注意大小写。JSON键名是区分大小写的。"Temp_C""temp_C" 是不同的键。

4. 混淆列表和字典

错误现象: 对列表使用 get value for key 或对字典使用 select list item,导致运行时错误。

解决方法:

  • JSON数组([])→ 列表 → 用 select list item 按索引访问
  • JSON对象({})→ 字典 → 用 get value for key 按键名访问
  • 可用 is a dictionary?is a list? 先判断类型

5. 索引越界

错误现象: 访问列表中不存在的索引(如对3个元素的列表取第4项)。

解决方法: 先用 length of list 检查列表长度,或使用 is in list 判断。

6. 中文编码问题

错误现象: 返回的中文显示为乱码。

解决方法: 使用 PostTextWithEncoding 并指定 UTF-8 编码;或在收到响应后,确保使用正确的文本编码。App Inventor默认使用UTF-8,通常不会有此问题。

7. 异步请求陷阱

错误现象: 在调用 Web1.Get() 之后立即访问全局变量,但数据还没回来。

解决方法: Web请求是异步的。所有数据操作必须在 GotText 事件中完成,不能在调用 Get() 之后立即处理数据。

// ❌ 错误示范
当 按钮.被点击 时
  调用 Web1.Get()
  设置 Label1.文本 为 全局变量结果  // 此时数据还没返回!

// ✅ 正确做法
当 按钮.被点击 时
  调用 Web1.Get()

当 Web1.GotText(...) 时
  // 在这里处理返回的数据
  设置 Label1.文本 为 解析后的结果

调试技巧

1. 打印JSON原始内容

GotText 事件中,先将 responseContent 显示到Label或通过 Notifier 弹窗显示,确认返回了什么数据:

当 Web1.GotText( url, responseCode, responseType, responseContent ) 时
  设置 DebugLabel.文本 为 responseContent

2. 检查解析后的类型

设 变量 data 为 调用 Web1.JsonTextDecodeWithDictionaries( responseContent )
如果 is a dictionary?( data ) 则
  设置 DebugLabel.文本 为 "这是一个字典"
否则 如果 is a list?( data ) 则
  设置 DebugLabel.文本 为 "这是一个列表"

3. 查看字典的所有键

设 变量 keys 为 get keys 从字典: data
设置 DebugLabel.文本 为 join with separator(", ", keys)
// 输出所有键名,帮助定位正确的字段

总结

场景 使用方法
解析JSON文本 Web1.JsonTextDecodeWithDictionaries(jsonText)
获取简单字段 get value for key: "key名" 从字典: dict
获取嵌套字段 get value at key path: ["层级1", "层级2", ...] 从字典: dict
遍历JSON数组 for each item in list 循环
批量提取字段 list by walking key path + walk all at level
发送GET请求 Web1.Get()
发送POST请求 Web1.PostText(jsonText)

推荐流程: 调用API → 检查状态码 → 解析JSON → 提取数据 → 显示结果


本文参考了 MIT App Inventor官方文档 - DictionariesUsing Web APIs with JSON


© 2025 AppInventor2中文网. 本教程基于 MIT App Inventor 官方文档编写,遵循 Creative Commons Attribution-ShareAlike 4.0 International License 协议。

文档反馈