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

如何用Chainlink Any API将JSON数组转为Solidity结构体列表?

问题描述

我有一个返回如下JSON数组的API:

[
  {
    "id": 2435,
    "name": "First Last",
    "email": "xxx@gmail.com",
    "gender": "male",
    "status": "inactive"
  },
  ...
]

我尝试使用Chainlink Any API将响应填充到合约的结构体列表变量(users)中,以下是我的合约代码:

pragma solidity ^0.8.7;
import '@chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';

contract FetchFromArray is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;

string public jsonData;
bytes32 private jobId;
uint256 private fee;
User[] public users;    

struct User{
  uint256 id;
  uint256 name;
  uint256 email;
  uint256 gender;
  uint256 status;
}

event GetUsersData(bytes32 indexed requestId, string jsonData);

/**
 * @notice Initialize the link token and target oracle
 */
constructor() ConfirmedOwner(msg.sender) {
    setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
    setChainlinkOracle(0xCC79157eb46F5624204f47AB42b3906cAA40eaB7);
    jobId = '7d80a6386ef543a3abb52817f6707e3b';
    fee = (1 * LINK_DIVISIBILITY) / 10; // 0.1 * 10**18 (Varies by network and job)
}

/**
 * Create a Chainlink request to retrieve API response
 */
function getUser() public returns (bytes32 requestId){
    Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);

    // Set the URL to perform the GET request on
    req.add('get', 'https://gorest.co.in/public/v2/users');
    req.add('path', '$');
    // Sends the request
    return sendChainlinkRequest(req, fee);
}

 /**
 * Receive the response in the form of string
 */
function fulfill(bytes32 _requestId, string memory _jsonData) public recordChainlinkFulfillment(_requestId) {
    emit GetUsersData(_requestId,_jsonData);
    jsonData = _jsonData;
    //  users = TO DO populate users list
}

 /**
 * Allow withdraw of Link tokens from the contract
 */
function withdrawLink() public onlyOwner {
    LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
    require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}

}

我遇到两个具体问题:

  1. 使用什么JSONPath表达式可以获取API的完整JSON响应?我试过req.add('path', '$')和req.add('path', '*'),但都无法返回JSON。
  2. 获取完整JSON并存储到jsonData变量后,如何解析它并将结果插入到User[] public users变量中?

解决方案

问题1:获取完整JSON响应的正确方式

你当前使用的Chainlink Job大概率不支持返回完整JSON字符串——多数默认HTTP Job的设计目标是提取单个字段(数值/字符串),而非返回整个响应体。

解决步骤:

  • 更换适配的Job:针对你合约配置的Polygon Mumbai网络,可使用支持返回完整JSON字符串的Job ID,比如29fa9aa13bf1468788b7cc4a500a45b8(该Job专门用于将整个API响应以string类型返回)。
  • JSONPath保持$:$是正确的JSONPath表达式,代表整个JSON根节点,不需要修改。

问题2:解析JSON并填充到User数组

Solidity本身没有内置JSON解析能力,且链上解析大数组会产生极高Gas成本,优先推荐用Chainlink External Adapter在链下完成解析,再将结构化数据传给合约。

方案1:使用External Adapter(生产环境首选)

1. 修正合约结构体

首先要纠正你合约里的类型错误:name、email、gender、status都是字符串类型,不能定义为uint256,需改为string:

struct User{
  uint256 id;
  string name;
  string email;
  string gender;
  string status;
}

2. 开发External Adapter

编写一个链下服务,完成以下逻辑:

  • 调用目标API获取JSON数组
  • 解析数组中的每个用户对象,提取id、name等字段
  • 将整理后的批量用户数据返回给Chainlink Oracle

3. 调整合约请求与fulfill函数

更换为适配External Adapter的Job后,修改fulfill函数以接收解析后的批量数据,直接填充到users数组:

// 示例:接收批量用户字段数组
function fulfill(
  bytes32 _requestId,
  uint256[] memory ids,
  string[] memory names,
  string[] memory emails,
  string[] memory genders,
  string[] memory statuses
) public recordChainlinkFulfillment(_requestId) {
  emit GetUsersData(_requestId, "Fulfilled users data");
  // 清空原有数据(可选)
  delete users;
  // 批量添加用户到数组
  for(uint256 i = 0; i < ids.length; i++){
    users.push(User({
      id: ids[i],
      name: names[i],
      email: emails[i],
      gender: genders[i],
      status: statuses[i]
    }));
  }
}

方案2:链上解析(仅测试场景使用)

如果必须在链上解析,可使用OpenZeppelin提供的JSONParser工具库,但注意Gas成本问题:

  1. 导入库:
import "@openzeppelin/contracts/utils/json/JSONParser.sol";
  1. 修改fulfill函数实现解析逻辑:
function fulfill(bytes32 _requestId, string memory _jsonData) public recordChainlinkFulfillment(_requestId) {
  emit GetUsersData(_requestId, _jsonData);
  jsonData = _jsonData;
  
  // 解析JSON数组
  (bool success, JSONParser.List memory usersList) = JSONParser.parseList(_jsonData);
  require(success, "Failed to parse JSON array");
  
  delete users;
  // 遍历数组解析每个用户对象
  for(uint256 i = 0; i < usersList.length(); i++){
    (success, JSONParser.Object memory userObj) = usersList.getObject(i);
    require(success, "Failed to fetch user object");
    
    // 解析各字段
    (success, uint256 id) = userObj.getUint("id");
    require(success, "Failed to parse id");
    
    (success, string memory name) = userObj.getString("name");
    require(success, "Failed to parse name");
    
    (success, string memory email) = userObj.getString("email");
    (success, string memory gender) = userObj.getString("gender");
    (success, string memory status) = userObj.getString("status");
    
    // 添加到users数组
    users.push(User({
      id: id,
      name: name,
      email: email,
      gender: gender,
      status: status
    }));
  }
}

内容的提问来源于stack exchange,提问作者aminedev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:50:28