You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

报错原因分析

  1. 异步错误无法被外层try-catch捕获:原代码中fetch的then回调里的异步操作(forEach中的await)抛出的错误,不在外层try的同步执行上下文里,导致错误被隐藏,无法直接定位行号。
  2. 未做存在性检查导致调用空值方法:如果接口返回的XML结构变化,或者解析后的JSON中jsonData.Tarih_Date、jsonData.Tarih_Date.Currency不存在,currencyData会是undefined或null,此时调用forEach就会触发TypeError。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 06:50:53