什么是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."
}
步骤
- 使用Web组件发送GET请求
- 在GotText事件中,用
JsonTextDecodeWithDictionaries解析响应内容 - 用
get value for key提取具体字段
积木块伪代码
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
var jsonData = Web1.JsonTextDecodeWithDictionaries(responseContent)
var title = getDictionaryValue(jsonData, "title")
var body = getDictionaryValue(jsonData, "body")
Label1.Text = title
Label2.Text = body
} else {
Label1.Text = join("请求失败,状态码:", 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数组(列表)。
方法一:逐层获取
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
var data = Web1.JsonTextDecodeWithDictionaries(responseContent)
// 第一层:获取name
var name = getDictionaryValue(data, "name")
// 第二层:先获取school字典,再从中获取name
var school = getDictionaryValue(data, "school")
var schoolName = getDictionaryValue(school, "name")
Label1.Text = name
Label2.Text = schoolName
}
}
方法二:使用 get value at key path(推荐)
get value at key path 可以通过一个路径列表一次性深入嵌套结构,更加简洁:
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
var data = Web1.JsonTextDecodeWithDictionaries(responseContent)
// 使用键路径直接获取嵌套值
var schoolName = getDictionaryValueAtPath(data, createList("school", "name"))
Label1.Text = 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 循环
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
var data = Web1.JsonTextDecodeWithDictionaries(responseContent)
var people = getDictionaryValue(data, "people")
var names = createList()
foreach person in people {
var firstName = getDictionaryValue(person, "first_name")
addItemsToList(names, firstName)
}
Label1.Text = joinItems(names, ", ")
}
}
方法二:使用 list by walking key path(高级技巧)
list by walking key path 配合 walk all at level 可以一步到位:
提取所有 first_name:
global data = Web1.JsonTextDecodeWithDictionaries(global_responseContent)
global firstNames = walkKeyPath(global_data, createList("people", "walk all at level", "first_name"))
// firstNames = ["Tim", "John", "Jane"]
解释:
"people"— 进入people键,得到一个列表walk all at level— 遍历列表中的每一个元素(每个字典)"first_name"— 从每个字典中取first_name键的值
混合字典和数组的路径
如果 JSON 数据为:
{
"id": 1,
"name": "Tim the Beaver",
"classes": ["6.001", "18.01", "8.01"]
}
要获取第二门课程("18.01"),可以用数字索引:
global secondClass = getDictionaryValueAtPath(global_data, createList("classes", 2))
// secondClass = "18.01"
注意:App Inventor的列表索引从 1 开始,不是从 0 开始。
实战案例:调用天气API获取天气数据
需求描述
调用一个免费的天气API,获取指定城市的天气信息并显示。
UI设计
| 组件 | 名称 | 用途 |
|---|---|---|
| TextBox | 文本输入框_城市 | 输入城市名 |
| Button | 按钮_查询 | 查询按钮 |
| Label | 标签_温度 | 显示温度 |
| Label | DescLabel | 显示天气描述 |
| Label | HumidityLabel | 显示湿度 |
| Web | Web客户端1 | 网络请求组件 |
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"}]
}
]
}
完整积木块伪代码
按钮点击 — 发送请求:
when Button1.Click() {
var city = TextBox1.Text
if city != "" {
Web1.Url = join("https://wttr.in/", city, "?format=j1")
Web1.Get()
} else {
Label1.Text = "请输入城市名"
}
}
接收响应 — 解析JSON:
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
// 第一步:将JSON文本解析为字典
var data = Web1.JsonTextDecodeWithDictionaries(responseContent)
// 第二步:获取 current_condition 数组的第一个元素
var conditions = getDictionaryValue(data, "current_condition")
var current = selectListItem(conditions, 1)
// 第三步:从当前天气字典中提取各字段
var temp = getDictionaryValue(current, "temp_C")
var humidity = getDictionaryValue(current, "humidity")
var feelsLike = getDictionaryValue(current, "FeelsLikeC")
// 第四步:获取天气描述(嵌套更深一层)
var descList = getDictionaryValue(current, "weatherDesc")
var descItem = selectListItem(descList, 1)
var desc = getDictionaryValue(descItem, "value")
// 第五步:显示结果
Label1.Text = join("温度:", temp, "°C(体感 ", feelsLike, "°C)")
DescLabel.Text = join("天气:", desc)
HumidityLabel.Text = join("湿度:", humidity, "%")
} else {
Label1.Text = join("请求失败: ", responseCode)
}
}
使用 get value at key path 简化
上面的步骤二、三可以简化为:
// 直接用键路径获取温度
global temp = getDictionaryValueAtPath(global_data, createList("current_condition", 1, "temp_C"))
// 直接用键路径获取湿度
global humidity = getDictionaryValueAtPath(global_data, createList("current_condition", 1, "humidity"))
使用 list by walking key path 提取多个城市数据
如果返回了多个城市的数据(数组),可以批量提取:
global allTemps = walkKeyPath(global_data, createList("current_condition", "walk all at level", "temp_C"))
// 得到所有城市的温度列表
常用字典积木块参考
创建字典
global dict = createDictionary(createPair("name", "Alice"), createPair("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。
解决方法: 始终使用 JsonTextDecodeWithDictionaries。JsonTextDecode 是旧版方法,将JSON对象转换为列表对形式(与 lookup in pairs 配合使用),不推荐用于新项目。
2. 忘记检查 responseCode
错误现象: 直接解析 responseContent,但请求可能失败(404、500等),此时 responseContent 不是有效JSON。
解决方法: 始终先判断 responseCode = 200 再解析:
when Web1.GotText(url, responseCode, responseType, responseContent) {
if responseCode == 200 {
// 解析JSON
} else {
// 提示错误
}
}
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. 异步请求陷阱
错误现象: 在调用 Web客户端1.Get() 之后立即访问全局变量,但数据还没回来。
解决方法: Web请求是异步的。所有数据操作必须在 GotText 事件中完成,不能在调用 Get() 之后立即处理数据。
// ❌ 错误示范
when Button1.Click() {
Web1.Get()
Label1.Text = global_result // 此时数据还没返回!
}
// ✅ 正确做法
when Button2.Click() {
Web1.Get()
}
when Web1.GotText(url, responseCode, responseType, responseContent) {
// 在这里处理返回的数据
Label1.Text = global_parsedResult
}
调试技巧
1. 打印JSON原始内容
在 GotText 事件中,先将 responseContent 显示到Label或通过 Notifier 弹窗显示,确认返回了什么数据:
when Web1.GotText(url, responseCode, responseType, responseContent) {
Label1.Text = responseContent
}
2. 检查解析后的类型
global data = Web1.JsonTextDecodeWithDictionaries(global_responseContent)
if isDictionary(global_data) {
Label1.Text = "这是一个字典"
} else if isList(global_data) {
Label1.Text = "这是一个列表"
}
3. 查看字典的所有键
global keys = getDictionaryKeys(global_data)
Label1.Text = joinItems(global_keys, ", ")
// 输出所有键名,帮助定位正确的字段
总结
| 场景 | 使用方法 |
|---|---|
| 解析JSON文本 | Web客户端1.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请求 | Web客户端1.Get() |
| 发送POST请求 | Web客户端1.PostText(jsonText) |
推荐流程: 调用API → 检查状态码 → 解析JSON → 提取数据 → 显示结果
本文参考了 MIT App Inventor官方文档 - Dictionaries 和 Using Web APIs with JSON。
© 2025 AppInventor2中文网. 本教程基于 MIT App Inventor 官方文档编写,遵循 Creative Commons Attribution-ShareAlike 4.0 International License 协议。
扫码添加客服咨询