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返回的温度

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支持。

文档反馈