- 概述
- XML基础概念
- 方案一:正则表达式提取
- 方案二:WebView + JavaScript DOM解析
- 方案三:文本分割函数
- 实战案例:RSS订阅解析
- 实战案例:天气API XML解析
- 方案对比
- 常见问题
- 总结
概述
XML(eXtensible Markup Language)是一种常见的数据交换格式,许多Web API仍返回XML格式数据。App Inventor 2没有内置XML解析积木块,但可以通过以下几种方案解析XML数据:
- 正则表达式提取 — 最简单,适合结构简单的XML
- WebView + JavaScript DOM解析 — 最强大,使用浏览器的XML解析能力
- 文本分割函数 — 适合固定格式的XML响应
- 扩展组件 — 社区提供的XML解析扩展
XML基础概念
XML结构示例
<?xml version="1.0" encoding="UTF-8"?>
<weather>
<city id="101010100">
<name>北京</name>
<temp>25</temp>
<humidity>60</humidity>
<forecast>
<day date="2026-05-18">
<high>28</high>
<low>18</low>
<desc>晴转多云</desc>
</day>
<day date="2026-05-19">
<high>26</high>
<low>17</low>
<desc>多云</desc>
</day>
</forecast>
</city>
</weather>
XML核心术语
| 术语 | 说明 | 示例 |
|---|---|---|
| 元素(Element) | XML的基本组成单元 | <name>北京</name> |
| 标签(Tag) | 元素的开始和结束标记 | <name> </name> |
| 属性(Attribute) | 元素的附加信息 | id="101010100" |
| 根元素(Root) | XML的最外层元素 | <weather> |
| 子元素(Child) | 嵌套在元素内的元素 | <temp> 是 <city> 的子元素 |
方案一:正则表达式提取
适用场景
- XML结构简单、层级少
- 只需要提取少量字段
- 快速原型开发
示例:提取天气API返回的温度
when Web客户端1.GotText(响应内容) {
// 提取 <temp> 标签内容
// 设 temp = 从文本 响应内容 中提取正则表达式 "<temp>(.*?)</temp>" 的第1个捕获组
var temp = 响应内容
// 提取 <name> 标签内容
var cityName = 响应内容
标签_结果.Text = join(cityName, "温度:", temp, "°C")
}
提取带属性的值
// 提取属性值:date="2026-05-18"
global dateValue = ""
// 提取多个同名元素(如多个 <day>)
global dayList = make_list()
正则表达式常用模式
| 目标 | 正则表达式 | 说明 |
|---|---|---|
| 提取标签内容 | <tag>(.*?)</tag> |
非贪婪匹配 |
| 提取属性值 | attr="([^"]*)" |
匹配引号内内容 |
| 提取所有匹配 | 使用全局匹配 | 文本.提取所有正则匹配() |
方案二:WebView + JavaScript DOM解析
适用场景
- 复杂嵌套XML
- 需要精确解析多层结构
- 需要处理XML属性
- 数据量较大
实现步骤
步骤1:创建HTML解析器文件
创建 xml_parser.html 放到项目Assets中:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="result"></div>
<script>
function parseXML(xmlString) {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xmlString, "text/xml");
// 检查解析错误
var errorNode = xmlDoc.querySelector('parsererror');
if (errorNode) {
window.AppInventor.setWebViewString("ERROR:" + errorNode.textContent);
return;
}
return xmlDoc;
}
// 提取单个元素文本
function getElementText(xmlString, tagName) {
var xmlDoc = parseXML(xmlString);
if (!xmlDoc) return;
var elements = xmlDoc.getElementsByTagName(tagName);
if (elements.length > 0) {
window.AppInventor.setWebViewString(elements[0].textContent);
}
}
// 提取所有同名元素,返回JSON数组
function getAllElements(xmlString, tagName) {
var xmlDoc = parseXML(xmlString);
if (!xmlDoc) return;
var elements = xmlDoc.getElementsByTagName(tagName);
var result = [];
for (var i = 0; i < elements.length; i++) {
result.push(elements[i].textContent);
}
window.AppInventor.setWebViewString(JSON.stringify(result));
}
// 提取元素属性
function getAttribute(xmlString, tagName, attrName) {
var xmlDoc = parseXML(xmlString);
if (!xmlDoc) return;
var elements = xmlDoc.getElementsByTagName(tagName);
if (elements.length > 0) {
window.AppInventor.setWebViewString(elements[0].getAttribute(attrName));
}
}
// 提取嵌套结构为JSON
function xmlToJson(xmlString, parentTag) {
var xmlDoc = parseXML(xmlString);
if (!xmlDoc) return;
var parents = xmlDoc.getElementsByTagName(parentTag);
var result = [];
for (var i = 0; i < parents.length; i++) {
var obj = {};
var children = parents[i].children;
for (var j = 0; j < children.length; j++) {
obj[children[j].tagName] = children[j].textContent;
}
// 也获取属性
if (parents[i].attributes) {
for (var k = 0; k < parents[i].attributes.length; k++) {
obj["_" + parents[i].attributes[k].name] = parents[i].attributes[k].value;
}
}
result.push(obj);
}
window.AppInventor.setWebViewString(JSON.stringify(result));
}
</script>
</body>
</html>
步骤2:在App Inventor中使用
when Screen1.Initialize() {
Web浏览器1.HomeUrl = "file:///android_asset/xml_parser.html"
}
// 获取XML并解析
when Web客户端1.GotText(响应内容) {
// 将XML传给WebView解析
Web浏览器1.RunJavaScript(join("getElementText('", 替换特殊字符(响应内容), "', 'temp')"))
}
// 接收WebView解析结果
when Web浏览器1.WebViewStringChange(value) {
if (starts_with(value, "ERROR")) {
标签_结果.Text = "解析错误"
} else {
标签_结果.Text = join("温度:", value, "°C")
}
}
步骤3:解析复杂嵌套结构
when 按钮_获取预报.Click() {
Web客户端1.Get("http://api.weather.com/forecast")
}
when Web客户端1.GotText(响应内容) {
// 提取所有 <day> 元素为JSON数组
Web浏览器1.RunJavaScript(join("xmlToJson('", 替换特殊字符(响应内容), "', 'day')"))
}
when Web浏览器1.WebViewStringChange(value) {
// 值为JSON数组:[{"_date":"2026-05-18","high":"28","low":"18","desc":"晴转多云"}, ...]
var forecastList = Web1.JsonTextDecode(value)
// 遍历显示
var 日期1 = get_dictionary_value(select_list_item(forecastList, 1), "date")
var 高温1 = get_dictionary_value(select_list_item(forecastList, 1), "high")
}
替换特殊字符的辅助函数
XML中可能包含单引号,需要转义后传入JavaScript:
function 替换特殊字符(文本) {
var 结果 = replace_all(文本, "'", "\\'")
结果 = replace_all(结果, "\\n", "")
return 结果
}
方案三:文本分割函数
适用场景
- XML格式固定且简单
- 不想用WebView
- 只需要提取一两个字段
示例
when Web客户端1.GotText(响应内容) {
// 用 <temp> 作为分隔符
var parts = split(响应内容, "<temp>")
if (length_of_list(parts) > 1) {
var second = select_list_item(parts, 2)
var parts2 = split(second, "</temp>")
var temp值 = select_list_item(parts2, 1)
标签_温度.Text = join(temp值, "°C")
}
}
实战案例:RSS订阅解析
RSS是一种XML格式的新闻订阅源,解析RSS是XML解析的典型应用。
RSS XML结构
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>MIT App Inventor News</title>
<item>
<title>New Extension Released</title>
<link>https://example.com/news/1</link>
<description>A new extension for barcode scanning</description>
<pubDate>Mon, 18 May 2026 10:00:00 GMT</pubDate>
</item>
<item>
<title>Version 2.70 Update</title>
<link>https://example.com/news/2</link>
<description>Chart component improvements</description>
<pubDate>Sun, 17 May 2026 08:00:00 GMT</pubDate>
</item>
</channel>
</rss>
积木块实现
when Screen1.Initialize() {
Web浏览器1.HomeUrl = "file:///android_asset/xml_parser.html"
Web客户端1.Get("https://example.com/rss.xml")
}
when Web客户端1.GotText(响应内容) {
Web浏览器1.RunJavaScript(join("xmlToJson('", 替换特殊字符(响应内容), "', 'item')"))
}
when Web浏览器1.WebViewStringChange(value) {
var itemList = Web1.JsonTextDecode(value)
var 显示文本 = ""
var i = 1
while (i <= length_of_list(itemList)) {
var item = select_list_item(itemList, i)
var title = get_dictionary_value(item, "title")
var desc = get_dictionary_value(item, "description")
var 日期 = get_dictionary_value(item, "pubDate")
显示文本 = join(显示文本, title, "\n", desc, "\n", 日期, "\n\n")
i = i + 1
}
标签_新闻.Text = 显示文本
}
实战案例:天气API XML解析
和风天气API示例
when 按钮_查询天气.Click() {
var apiKey = "your_api_key"
var cityId = "101010100" // 北京
var url = join("https://devapi.qweather.com/v7/weather/now?location=", cityId, "&key=", apiKey, "&lang=zh")
Web客户端1.Get(url)
}
when Web客户端1.GotText(响应内容) {
// 注意:和风天气新版API返回JSON,这里演示旧版XML解析
// 使用正则提取
var temp = "extracted"
var text = "extracted"
标签_天气.Text = join("当前天气:", text, ",温度:", temp, "°C")
}
方案对比
| 方案 | 复杂度 | 能力 | 适用场景 |
|---|---|---|---|
| 正则表达式 | ⭐ | 中等 | 简单XML、少量字段提取 |
| WebView+JS DOM | ⭐⭐⭐ | 强大 | 复杂嵌套XML、大量数据 |
| 文本分割 | ⭐ | 弱 | 固定格式、快速提取 |
| 扩展组件 | ⭐⭐ | 中等 | 需要专业XML处理 |
常见问题
Q1: 为什么解析XML时出现乱码?
确保Web组件的请求头设置了正确的编码:
Web客户端1.RequestHeaders = make_list(make_list("Accept-Charset", "UTF-8"))
如果API返回的是GBK编码,需要先转码或在WebView中指定编码。
Q2: 正则表达式提取不到内容怎么办?
常见原因:
- XML中有命名空间前缀(如
<ws:temp>),需要在正则中包含 - 标签跨行,
.默认不匹配换行符 - 大小写不匹配
Q3: WebView解析报parsererror?
XML格式不规范(未闭合标签、特殊字符未转义等)。在JavaScript中先检查错误:
var errorNode = xmlDoc.querySelector('parsererror');
if (errorNode) {
window.AppInventor.setWebViewString("ERROR");
return;
}
Q4: 如何处理大型XML文件?
对于大XML(>1MB):
- 使用WebView方案(浏览器DOM解析效率高)
- 避免在App Inventor变量中保存完整XML文本
- 分段解析,只提取需要的部分
Q5: 有没有XML解析扩展?
MIT App Inventor官方没有提供XML解析扩展,但社区中有一些第三方扩展:
- 在 AppyBuilder 或 Kodular 社区搜索 “XML Parser Extension”
- Niotron IDE 提供了内置的 XML 解析组件
总结
App Inventor 2虽然没有内置XML解析组件,但通过正则表达式、WebView+JavaScript、文本分割等方案完全可以处理常见的XML数据。推荐方案:
- 简单提取 → 正则表达式
- 复杂解析 → WebView + JavaScript DOMParser
- 固定格式 → 文本分割函数
对于新项目,建议优先选择返回JSON格式的API,因为App Inventor 2内置了完善的字典/JSON支持。
扫码添加客服咨询