App Inventor 2 XML数据解析教程 - 从Web API获取和解析XML

« 返回首页

概述

XML(eXtensible Markup Language)是一种常见的数据交换格式,许多Web API仍返回XML格式数据。App Inventor 2没有内置XML解析积木块,但可以通过以下几种方案解析XML数据:

  1. 正则表达式提取 — 最简单,适合结构简单的XML
  2. WebView + JavaScript DOM解析 — 最强大,使用浏览器的XML解析能力
  3. 文本分割函数 — 适合固定格式的XML响应
  4. 扩展组件 — 社区提供的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返回的温度

当 Web1.收到文本(响应内容)
  // 提取 <temp> 标签内容
  设 temp = 从文本 响应内容 中提取正则表达式 "<temp>(.*?)</temp>" 的第1个捕获组
  
  // 提取 <name> 标签内容
  设 cityName = 从文本 响应内容 中提取正则表达式 "<name>(.*?)</name>" 的第1个捕获组
  
  设 标签_结果.文本 = cityName & "温度:" & temp & "°C"

提取带属性的值

// 提取属性值:date="2026-05-18"
设 dateValue = 从文本 xml文本 中提取正则表达式 'date="([^"]*)"' 的第1个捕获组

// 提取多个同名元素(如多个 <day>)
设 dayList = 从文本 xml文本 中提取正则表达式 "<day.*?>(.*?)</day>" 的所有匹配项

正则表达式常用模式

目标 正则表达式 说明
提取标签内容 <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中使用

当 Screen1.初始化
  设 WebView1.主页地址 = "file:///android_asset/xml_parser.html"

// 获取XML并解析
当 Web1.收到文本(响应内容)
  // 将XML传给WebView解析
  调用 WebView1.运行JavaScript("getElementText('" & 替换特殊字符(响应内容) & "', 'temp')")

// 接收WebView解析结果
当 WebView1.WebView字符串改变(值)
  如果 值 以 "ERROR" 开头
    标签_结果.文本 = "解析错误"
  否则
    标签_结果.文本 = "温度:" & 值 & "°C"

步骤3:解析复杂嵌套结构

当 按钮_获取预报.被点击
  调用 Web1.执行Get请求("http://api.weather.com/forecast")

当 Web1.收到文本(响应内容)
  // 提取所有 <day> 元素为JSON数组
  调用 WebView1.运行JavaScript("xmlToJson('" & 替换特殊字符(响应内容) & "', 'day')")

当 WebView1.WebView字符串改变(值)
  // 值为JSON数组:[{"_date":"2026-05-18","high":"28","low":"18","desc":"晴转多云"}, ...]
  设 forecastList = 调用 文本.从JSON解析(值)
  // 遍历显示
  设 日期1 = 从字典 forecastList 的第1项 获取 "date"
  设 高温1 = 从字典 forecastList 的第1项 获取 "high"

替换特殊字符的辅助函数

XML中可能包含单引号,需要转义后传入JavaScript:

定义 替换特殊字符(文本) 返回 结果
  设 结果 = 替换全部 文本 中的 "'" 为 "\\'"
  设 结果 = 替换全部 结果 中的 换行符 为 ""
  返回 结果

方案三:文本分割函数

适用场景

  • XML格式固定且简单
  • 不想用WebView
  • 只需要提取一两个字段

示例

当 Web1.收到文本(响应内容)
  // 用 <temp> 作为分隔符
  设 parts = 分割文本 响应内容 以 "<temp>"
  如果 列表长度(parts) > 1
    设 second = 列表第2项(parts)
    设 parts2 = 分割文本 second 以 "</temp>"
    设 temp值 = 列表第1项(parts2)
    标签_温度.文本 = 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>

积木块实现

当 Screen1.初始化
  设 WebView1.主页地址 = "file:///android_asset/xml_parser.html"
  调用 Web1.执行Get请求("https://example.com/rss.xml")

当 Web1.收到文本(响应内容)
  调用 WebView1.运行JavaScript("xmlToJson('" & 替换特殊字符(响应内容) & "', 'item')")

当 WebView1.WebView字符串改变(值)
  设 itemList = 调用 文本.从JSON解析(值)
  设 显示文本 = ""
  设 i = 1
  当 i ≤ 列表长度(itemList) 执行
    设 item = 列表第i项(itemList)
    设 title = 从字典 item 获取 "title"
    设 desc = 从字典 item 获取 "description"
    设 日期 = 从字典 item 获取 "pubDate"
    设 显示文本 = 显示文本 & title & "\n" & desc & "\n" & 日期 & "\n\n"
    设 i = i + 1
  
  标签_新闻.文本 = 显示文本

实战案例:天气API XML解析

和风天气API示例

当 按钮_查询天气.被点击
  设 apiKey = "your_api_key"
  设 cityId = "101010100"  ' 北京
  设 url = "https://devapi.qweather.com/v7/weather/now?location=" & cityId & "&key=" & apiKey & "&lang=zh"
  调用 Web1.执行Get请求(url)

当 Web1.收到文本(响应内容)
  // 注意:和风天气新版API返回JSON,这里演示旧版XML解析
  // 使用正则提取
  设 temp = 从文本 响应内容 中提取正则表达式 '"temp":"([^"]*)"' 的第1个捕获组
  设 text = 从文本 响应内容 中提取正则表达式 '"text":"([^"]*)"' 的第1个捕获组
  
  标签_天气.文本 = "当前天气:" & text & ",温度:" & temp & "°C"

方案对比

方案 复杂度 能力 适用场景
正则表达式 中等 简单XML、少量字段提取
WebView+JS DOM ⭐⭐⭐ 强大 复杂嵌套XML、大量数据
文本分割 固定格式、快速提取
扩展组件 ⭐⭐ 中等 需要专业XML处理

常见问题

Q1: 为什么解析XML时出现乱码?

确保Web组件的请求头设置了正确的编码:

调用 Web1.设置请求头("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解析扩展,但社区中有一些第三方扩展:

  • AppyBuilderKodular 社区搜索 “XML Parser Extension”
  • Niotron IDE 提供了内置的 XML 解析组件

总结

App Inventor 2虽然没有内置XML解析组件,但通过正则表达式、WebView+JavaScript、文本分割等方案完全可以处理常见的XML数据。推荐方案:

  • 简单提取 → 正则表达式
  • 复杂解析 → WebView + JavaScript DOMParser
  • 固定格式 → 文本分割函数

对于新项目,建议优先选择返回JSON格式的API,因为App Inventor 2内置了完善的字典/JSON支持。

版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。

文档反馈