gpt4 book ai didi

javascript - 如何使用复杂的嵌套和未命名数组解析分页的 JSON API 响应?

转载 作者:行者123 更新时间:2023-12-04 07:53:37 25 4
gpt4 key购买 nike

我已经构建了(在@EmielZuurbier 的帮助下)一个发票模板,它向 Quickbase 发出 API 调用。 API 响应是分页的。如何将分页响应解析为单个表?

  • Quickbase API 端点:https://developer.quickbase.com/operation/runQuery
  • Quickbase 分页元数据解释:https://developer.quickbase.com/pagination

  • API 调用的响应如下所示(我删除了 data 下的大部分项目,否则在 stackoverflow 上发帖会很长时间)
    {
    "data": [
    {
    "15": {
    "value": "F079427"
    },
    "19": {
    "value": 50.0
    },
    "48": {
    "value": "(S1)"
    },
    "50": {
    "value": "2021-03-01"
    },
    "8": {
    "value": "71 Wauregan Rd, Danielson, Connecticut 06239"
    }
    },
    {
    "15": {
    "value": "F079430"
    },
    "19": {
    "value": 50.0
    },
    "48": {
    "value": "(S1)"
    },
    "50": {
    "value": "2021-03-01"
    },
    "8": {
    "value": "7 County Home Road, Thompson, Connecticut 06277"
    }
    },
    {
    "15": {
    "value": "F079433"
    },
    "19": {
    "value": 50.0
    },
    "48": {
    "value": "(S1)"
    },
    "50": {
    "value": "2021-03-16"
    },
    "8": {
    "value": "12 Bentwood Street, Foxboro, Massachusetts 02035"
    }
    }
    ],
    "fields": [
    {
    "id": 15,
    "label": "Project Number",
    "type": "text"
    },
    {
    "id": 8,
    "label": "Property Adress",
    "type": "address"
    },
    {
    "id": 50,
    "label": "Date Completed",
    "type": "text"
    },
    {
    "id": 48,
    "label": "Billing Codes",
    "type": "text"
    },
    {
    "id": 19,
    "label": "Total Job Price",
    "type": "currency"
    }
    ],
    "metadata": {
    "numFields": 5,
    "numRecords": 500,
    "skip": 0,
    "totalRecords": 766
    }
    }
    以下是我使用的完整 javascript 代码
    const urlParams = new URLSearchParams(window.location.search);
    //const dbid = urlParams.get('dbid');//
    //const fids = urlParams.get('fids');//
    let rid = urlParams.get('rid');
    //const sortLineItems1 = urlParams.get('sortLineItems1');//
    //const sortLineItems2 = urlParams.get('sortLineItems2');//
    let subtotalAmount = urlParams.get('subtotalAmount');
    let discountAmount = urlParams.get('discountAmount');
    let creditAmount = urlParams.get('creditAmount');
    let paidAmount = urlParams.get('paidAmount');
    let balanceAmount = urlParams.get('balanceAmount');
    let clientName = urlParams.get('clientName');
    let clientStreetAddress = urlParams.get('clientStreetAddress');
    let clientCityStatePostal = urlParams.get('clientCityStatePostal');
    let clientPhone = urlParams.get('clientPhone');
    let invoiceNumber = urlParams.get('invoiceNumber');
    let invoiceTerms = urlParams.get('invoiceTerms');
    let invoiceDate = urlParams.get('invoiceDate');
    let invoiceDueDate = urlParams.get('invoiceDueDate');
    let invoiceNotes = urlParams.get('invoiceNotes');


    const formatCurrencyUS = function (x) {
    return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(x);
    }


    let subtotalAmountFormatted = formatCurrencyUS(subtotalAmount);
    let discountAmountFormatted = formatCurrencyUS(discountAmount);
    let creditAmountFormatted = formatCurrencyUS(creditAmount);
    let paidAmountFormatted = formatCurrencyUS(paidAmount);
    let balanceAmountFormatted = formatCurrencyUS(balanceAmount);


    document.getElementById("subtotalAmount").innerHTML = `${subtotalAmountFormatted}`;
    document.getElementById("discountAmount").innerHTML = `${discountAmountFormatted}`;
    document.getElementById("creditAmount").innerHTML = `${creditAmountFormatted}`;
    document.getElementById("paidAmount").innerHTML = `${paidAmountFormatted}`;
    document.getElementById("balanceAmount").innerHTML = `${balanceAmountFormatted}`;
    document.getElementById("clientName").innerHTML = `${clientName}`;
    document.getElementById("clientStreetAddress").innerHTML = `${clientStreetAddress}`;
    document.getElementById("clientCityStatePostal").innerHTML = `${clientCityStatePostal}`;
    document.getElementById("clientPhone").innerHTML = `${clientPhone}`;
    document.getElementById("invoiceNumber").innerHTML = `${invoiceNumber}`;
    document.getElementById("invoiceTerms").innerHTML = `${invoiceTerms}`;
    document.getElementById("invoiceDate").innerHTML = `${invoiceDate}`;
    document.getElementById("invoiceDueDate").innerHTML = `${invoiceDueDate}`;
    document.getElementById("invoiceNotes").innerHTML = `${invoiceNotes}`;


    let headers = {
    'QB-Realm-Hostname': 'XXXXX',
    'User-Agent': 'Invoice',
    'Authorization': 'XXXXX',
    'Content-Type': 'application/json'
    }


    let body =

    {
    "from": "bq9dajvu5",
    "select": [
    15,
    8,
    50,
    48,
    19
    ],
    "where": `{25.EX.${rid}}`,
    "sortBy": [
    {
    "fieldId": 50,
    "order": "ASC"
    },
    {
    "fieldId": 8,
    "order": "ASC"
    }
    ],
    "options": {
    "skip": 0
    }
    }


    const xmlHttp = new XMLHttpRequest();
    xmlHttp.open('POST', 'https://api.quickbase.com/v1/records/query', true);
    for (const key in headers) {
    xmlHttp.setRequestHeader(key, headers[key]);
    }


    xmlHttp.onreadystatechange = function () {
    if (xmlHttp.readyState === XMLHttpRequest.DONE) {
    console.log(xmlHttp.responseText);


    let line_items = JSON.parse(this.responseText);
    console.log(line_items);





    const transformResponseData = (line_items) => {
    const { data, fields } = line_items;

    //***Return a new array with objects based on the values of the data and fields arrays***//
    const revivedData = data.map(entry =>
    fields.reduce((object, { id, label }) => {
    object[label] = entry[id].value;
    return object;
    }, {})
    );

    //***Combine the original object with the new data key***//
    return {
    ...line_items,
    data: revivedData
    };
    };





    const createTable = ({ data, fields }) => {
    const table = document.getElementById('line_items'); //const table = document.createElement('table');
    const tHead = document.getElementById('line_items_thead'); //const tHead = table.createTHead();
    const tBody = document.getElementById('line_items_tbody'); //const tBody = table.createTBody();
    //***Create a head for each label in the fields array***//
    const tHeadRow = tHead.insertRow();



    // ***Create the counts cell manually***//
    const tHeadRowCountCell = document.createElement('th');
    tHeadRowCountCell.textContent = 'Count';
    tHeadRow.append(tHeadRowCountCell);



    for (const { label } of fields) {
    const tHeadRowCell = document.createElement('th');
    tHeadRowCell.textContent = label;
    tHeadRow.append(tHeadRowCell);
    }


    // Output all the values of the new data array//
    for (const [index, entry] of data.entries()) {
    const tBodyRow = tBody.insertRow();

    // Create a new array with the index and the values from the object//
    const values = [
    index + 1,
    ...Object.values(entry)
    ];

    // Loop over the combined values array//
    for (const [index, value] of values.entries()) {
    const tBodyCell = tBodyRow.insertCell();
    tBodyCell.textContent = index === 5 ?
    Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value) ://value.toFixed(2) :
    value;


    }
    }
    return table;
    };





    const data = transformResponseData(line_items);
    const table = createTable(data);
    document.getElementById("line_items_div").append(table) //.innerHTML = table <-- this does not work// //document.body.append(table);
    console.log(data);





    }
    };

    xmlHttp.send(JSON.stringify(body));
    这就是我想要实现的(地址仅显示为 xxx,因此该表非常适合 stackoverflow)


    数数
    项目编号
    属性(property)地址
    完成日期
    计费代码
    总工作价格


    1
    F079427
    xxx
    2021-03-01
    (S1)
    50.00 美元

    2
    F079430
    xxx
    2021-03-01
    (S1)
    50.00 美元

    3
    F079433
    xxx
    2021-03-16
    (S1)
    50.00 美元


    我对如何做到这一点的想法
    对于请求公式,我们可能需要一个循环函数,它会跳过 === to the sum of all the numRecords for every request made until skip + numRecords === totalRecords 的记录量。
    例如,如果 totalRecords = 1700
  • 第一个请求 {"skip": 0}返回 numRecords=500
  • 第二个请求 {"skip": 500}返回 numRecords=500
  • 第三个请求 {"skip": 1000}返回 numRecords=500
  • 第四次请求 {"skip": 1500}返回 numRecords=200

  • 关于第四个请求 skip + numRecords = 1700这等于总记录,因此循环应该停止。
    在我们拥有所有这些数组之后,我们以某种方式将它们合并到一个表中,这是比我熟悉的更高级的 javascript。

    最佳答案

    你的想法是正确的。 API 指示使用 skip基于 totalRecords 的请求中的功能和 numRecords响应元数据中的值。
    要进行设置,您需要三个部分。
    首先,您的 headersbody . headers将保持不变,因为每个请求都需要相同。body将获得跳过值,但是每个请求的值都不同,因此我们将在发出请求时添加该部分。

    const headers = {
    'QB-Realm-Hostname': 'XXXXX',
    'User-Agent': 'Invoice',
    'Authorization': 'XXXXX',
    'Content-Type': 'application/json'
    };

    const body = {
    "from": "bq9dajvu5",
    "select": [
    15,
    8,
    50,
    48,
    19
    ],
    "where": `{25.EX.${rid}}`,
    "sortBy": [
    {
    "fieldId": 50,
    "order": "ASC"
    },
    {
    "fieldId": 8,
    "order": "ASC"
    }
    ] // options object will be added later.
    };
    第二部分是重写您的请求脚本,以便我们可以传递 skip value 并将其放入请求正文中。我确实看到你在使用 XMLHttpRequest() ,但我建议您查看较新的 Fetch API。它基本上是一样的,但有不同的,在我看来,更易读的语法。
    因为 skip值(value)是动态的,我们建立起来 body通过组合 body 的属性来处理请求对象,带有 options属性,其中包含 skip属性(property)和值(value)。
    /**
    * Makes a single request to the records/query endpoint.
    * Expects a JSON response.
    *
    * @param {number} [skip=0] Amount of records to skip in the request.
    * @returns {any}
    */
    const getRecords = async (skip = 0) => {
    const url = 'https://api.quickbase.com/v1/records/query';

    // Make the request with the skip value included.
    const response = await fetch(url, {
    method: 'POST',
    headers,
    body: JSON.stringify({
    ...body,
    "options": {
    "skip": skip
    }
    })
    });

    // Check if the response went okay, if not, throw an error.
    if (!response.ok) {
    throw new Error(`
    The getRecords request has failed:
    ${response.status} - ${response.statusText}
    `);
    }

    // Decode the body of the response
    const payload = await response.json();
    return payload;
    };
    最后一部分是关于确保 getRecords如果 API 需要更多记录,则函数会不断被调用。
    为此,我创建了一个递归函数,这意味着它会一直调用自己直到满足条件。在这种情况下,我们希望继续调用该函数,直到没有更多记录要获取。
    每当没有更多请求时,它将返回一个对象,与原始响应类似,但带有所有 data数组组合。
    因此,这意味着您将拥有相同的结构,而无需执行任何其他操作来展平或重组数组以创建表。
    /**
    * Recursive function which keeps getting more records if the current amount
    * of records is below the total. Then skips the amount already received
    * for each new request, collecting all data in a single object.
    *
    * @param {number} amountToSkip Amount of records to skip.
    * @param {object} collection The collection object.
    * @returns {object} An object will all data collected.
    */
    const collectRecords = async (amountToSkip = 0, collection = { data: [], fields: [] }) => {
    try {
    const { data, fields, metadata } = await getRecords(amountToSkip);
    const { numRecords, totalRecords, skip } = metadata;

    // The amount of collected records.
    const recordsCollected = numRecords + skip;

    // The data array should be merged with the previous ones.
    collection.data = [
    ...collection.data,
    ...data
    ];

    // Set the fields the first time.
    // They'll never change and only need to be set once.
    if (!collection.fields.length) {
    collection.fields = fields;
    }

    // The metadata is updated for each request.
    // It might be useful to know the state of the last request.
    collection.metadata = metadata;

    // Get more records if the current amount of records + the skip amount is lower than the total.
    if (recordsCollected < totalRecords) {
    return collectRecords(recordsCollected, collection);
    }

    return collection;
    } catch (error) {
    console.error(error);
    }
    };
    现在要使用它,请调用 collectRecords函数然后将继续发出请求,直到没有更多请求为止。此函数将返回 Promise ,所以你必须使用 then Promise的方法告诉您在检索到所有记录时要执行的操作。
    就像等待一切结束和 然后 对数据做一些事情。
    // Select the table div element.
    const tableDiv = document.getElementById('line_items_div');

    // Get the records, collect them in multiple requests, and generate a table from the data.
    collectRecords().then(records => {
    const data = transformRecordsData(records);
    const table = createTable(data);
    tableDiv.append(table);
    });

    关于javascript - 如何使用复杂的嵌套和未命名数组解析分页的 JSON API 响应?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66820192/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com