JavaScript报错:无法将undefined/null转换为对象,求技术排查帮助
问题排查:TypeError: Undefined or null cannot be converted to object
问题描述
运行代码时触发异常,报错信息为TypeError: Undefined or null cannot be converted to object,无法定位具体报错原因及行号。代码功能为调用土耳其央行接口获取XML数据,转为JSON后存入数据库,相关代码如下:
接口调用与存库代码
try { fetch("https://www.tcmb.gov.tr/kurlar/today.xml") .then((response) => response.text()) .then((data) => { const parser = new DOMParser(); const xml = parser.parseFromString(data, "text/xml"); const jsonData = xmlToJson(xml); const currencyData = jsonData.Tarih_Date.Currency; currencyData.forEach(async (doviz) => { await db.collection("currencies").add({ name: doviz.CurrencyName, }); }); }) } catch (error) { console.error(error); }
XML转JSON函数
function xmlToJson(xml) { var obj = {}; if (xml.nodeType == 1) { if (xml.attributes.length > 0) { obj["@attributes"] = {}; for (var j = 0; j < xml.attributes.length; j++) { var attribute = xml.attributes.item(j); obj["@attributes"][attribute.nodeName] = attribute.nodeValue; } } } else if (xml.nodeType == 3) { obj = xml.nodeValue; } var textNodes = [].slice.call(xml.childNodes).filter(function(node) { return node.nodeType === 3; }); if (xml.hasChildNodes() && xml.childNodes.length === textNodes.length) { obj = [].slice.call(xml.childNodes).reduce(function(text, node) { return text + node.nodeValue; }, ""); } else if (xml.hasChildNodes()) { for (var i = 0; i < xml.childNodes.length; i++) { var item = xml.childNodes.item(i); var nodeName = item.nodeName; if (typeof obj[nodeName] == "undefined") { obj[nodeName] = xmlToJson(item); } else { if (typeof obj[nodeName].push == "undefined") { var old = obj[nodeName]; obj[nodeName] = []; obj[nodeName].push(old); } obj[nodeName].push(xmlToJson(item)); } } } return obj; }
报错原因分析
- 异步错误无法被外层try-catch捕获:原代码中
fetch的then回调里的异步操作(forEach中的await)抛出的错误,不在外层try的同步执行上下文里,导致错误被隐藏,无法直接定位行号。 - 未做存在性检查导致调用空值方法:如果接口返回的XML结构变化,或者解析后的JSON中
jsonData.Tarih_Date、jsonData.Tarih_Date.Currency不存在,currencyData会是undefined或null,此时调用forEach就会触发TypeError。 - xmlToJson函数的潜在问题:当XML中存在空文本节点或异常节点时,函数可能返回非预期的
null/undefined,后续访问属性时出错。
修复方案
方案1:添加存在性检查并优化异步错误捕获
改用async/await重构代码,让所有错误能被外层try-catch捕获,同时在访问嵌套属性和调用数组方法前做检查:
async function fetchAndSaveCurrencies() { try { const response = await fetch("https://www.tcmb.gov.tr/kurlar/today.xml"); const data = await response.text(); const parser = new DOMParser(); const xml = parser.parseFromString(data, "text/xml"); const jsonData = xmlToJson(xml); // 逐层检查属性是否存在 if (!jsonData?.Tarih_Date?.Currency) { console.error("解析后的数据结构不符合预期"); return; } const currencyData = jsonData.Tarih_Date.Currency; // 确保currencyData是数组再调用遍历方法 const currencyArray = Array.isArray(currencyData) ? currencyData : [currencyData]; for (const doviz of currencyArray) { // 检查doviz是否包含CurrencyName字段 if (doviz?.CurrencyName) { await db.collection("currencies").add({ name: doviz.CurrencyName, }); } else { console.warn("缺少CurrencyName字段", doviz); } } } catch (error) { console.error("执行出错:", error); } } // 调用函数 fetchAndSaveCurrencies();
方案2:增强xmlToJson函数的健壮性
在函数中添加空值检查,避免处理异常节点时返回非预期值:
function xmlToJson(xml) { if (!xml) return {}; // 处理xml为null/undefined的情况 var obj = {}; if (xml.nodeType == 1) { if (xml.attributes.length > 0) { obj["@attributes"] = {}; for (var j = 0; j < xml.attributes.length; j++) { var attribute = xml.attributes.item(j); if (attribute) { // 检查attribute是否存在 obj["@attributes"][attribute.nodeName] = attribute.nodeValue; } } } } else if (xml.nodeType == 3) { obj = xml.nodeValue?.trim() || ""; // 处理空文本节点 } var textNodes = [].slice.call(xml.childNodes || []).filter(function(node) { // 处理childNodes为空的情况 return node?.nodeType === 3; }); if (xml.hasChildNodes() && xml.childNodes.length === textNodes.length) { obj = [].slice.call(xml.childNodes || []).reduce(function(text, node) { return text + (node?.nodeValue || ""); }, ""); } else if (xml.hasChildNodes()) { for (var i = 0; i < (xml.childNodes?.length || 0); i++) { var item = xml.childNodes.item(i); if (!item) continue; // 跳过空节点 var nodeName = item.nodeName; if (typeof obj[nodeName] == "undefined") { obj[nodeName] = xmlToJson(item); } else { if (typeof obj[nodeName].push == "undefined") { var old = obj[nodeName]; obj[nodeName] = []; obj[nodeName].push(old); } obj[nodeName].push(xmlToJson(item)); } } } return obj; }
内容的提问来源于stack exchange,提问作者Barulay
相关产品推荐
相关产品推荐

