为了账号安全,请及时绑定邮箱和手机立即绑定

制作一个将 URL 字符串转换为 JSON 的递归算法

制作一个将 URL 字符串转换为 JSON 的递归算法

拉风的咖菲猫 2023-07-14 14:52:37
我发现自己必须处理如下字符串:foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar进入:{  "foo": "bar",  "foo1": {    "foo": "bar",    "foo2": {      "foo": "bar"    }  }}我最好的尝试是:function parse(input) {  try {    const parsed = JSON.parse(input);    return parseJSON(parsed);  } catch (err) {    const decodedInput = decodeURIComponent(input);    if (input.includes("&") && input.includes("=")) {      return input.split("&").reduce((json, part) => {        const [key, value] = part.split("=");        const decodedValue = decodeURIComponent(value);        return { ...json, [key]: parsePrimitive(decodedValue) };      }, {});    }    return decodedInput;  }}function parsePrimitive(input) {  if (!isNaN(input)) {    return Number(input);  }  if (input === "true" || input === "false") {    return input === "true";  }  return parse(input);}function parseJSON(input) {  return Object.entries(input).reduce((json, [key, value]) => {    let object = {};    if (typeof value === "object") {      if (Array.isArray(value)) {        object[key] = value;      } else {        object[key] = parseJSON(value);      }    } else {      const decodedValue = decodeURIComponent(value);      if (decodedValue.includes("&") && decodedValue.includes("=")) {        object[key] = parse(decodedValue);      } else {        object[key] = parsePrimitive(decodedValue);      }    }    return { ...json, ...object };  }, {});}如果您尝试运行它,您应该调用parse(input)但是,对于某些输入它确实会失败如何针对此类问题制定完美的递归算法?
查看完整描述

3 回答

?
桃花长相依

TA贡献1860条经验 获得超8个赞

这似乎适用于您的简单示例和更复杂的示例(现已更新以处理数字和布尔值):

const parse = (query) =>

  query .startsWith ('{')

    ? JSON .parse (query)

  : query .includes ('&') || query .includes ('=')

    ? Object .fromEntries (

        query .split ('&') 

          .map (p => p .split ('='))

          .map (([k, v]) => [k, parse (decodeURIComponent (v))])

      )

  : query .includes (',')

    ? query .split (',') .filter (Boolean) .map (parse)

  : isFinite (query)

    ? Number (query)

  : query .toLowerCase () == "true" || query .toLowerCase () == "false"

    ? query .toLowerCase () == "true"

  : // else

    query



const q = 'foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar'

console .log (parse(q))


console.log('fetching larger example...')


fetch ('https://gist.githubusercontent.com/avi12/cd1d6728445608d64475809a8ddccc9c/raw/030974baed3eaadb26d9378979b83b1d30a265a3/url-input-example.txt')

  .then (res => res .text ())

  .then (parse)

  .then (console .log)

.as-console-wrapper {max-height: 100% !important; top: 0}

有两个部分值得关注。


首先,这对逗号做了一个假设:它们表示数组元素之间的分隔。而且,更进一步,它假设空字符串不是有意的,从而将


watermark=%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_watermark-vflHX6b6E.png

%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_hd_watermark-vflAzLcD6.png

进入这个:


watermark: [

  "https://s.ytimg.com/yts/img/watermark/youtube_watermark-vflHX6b6E.png",

  "https://s.ytimg.com/yts/img/watermark/youtube_hd_watermark-vflAzLcD6.png"

]

原始内容以编码的逗号 ( %2C) 开头,这会导致初始空字符串,因此我们使用.filter (Boolean)删除它。


其次,对表示 JSON 的字符串的测试非常幼稚,仅执行.startsWith ('{'). 您可以将其替换为您需要的任何内容,但这会导致意图问题。我不确定我们是否可以以这种方式完全通用地编写此内容。


不过,我认为已经很接近了。而且代码相当干净。


然而,我确实想知道为什么。这么多数据将会遇到各种 url 大小限制。此时,将其放入请求正文而不是 url 参数不是更有意义吗?


查看完整回答
反对 回复 2023-07-14
?
POPMUISE

TA贡献1765条经验 获得超5个赞

您可以通过检查编码符号来采用递归方法=。


const getValues = string => string.split('&')

    .reduce((r, pair) => {

      let [key, value] = pair.split('=');

      value = decodeURIComponent(value);

      r[key] = value.includes('=')

        ? getValues(value)

        : value;

      return r;

    }, {});


console.log(getValues('foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar'));


查看完整回答
反对 回复 2023-07-14
?
呼啦一阵风

TA贡献1802条经验 获得超6个赞

我使用Object.fromEntries (new URLSearchParams ())重新设计了算法。

function parse(query) {

  try {

    return JSON.parse(query);

  } catch {

    if (!isNaN(query)) {

      return Number(query);

    }


    if (typeof query !== "string") {

      const obj = {};

      for (const queryKey in query) {

        if (query.hasOwnProperty(queryKey)) {

          obj[queryKey] = parse(query[queryKey]);

        }

      }


      return obj;

    }

    if (!query) {

      return "";

    }


    if (query.toLowerCase().match(/^(true|false)$/)) {

      return query.toLowerCase() === "true";

    }


    const object = Object.fromEntries(new URLSearchParams(query));

    const values = Object.values(object);

    if (values.length === 1 && values[0] === "") {

      return query;

    }

    return parse(object);

  }

}




const q = 'foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar';

console.log(parse(q));


console.log('fetching larger example...');


fetch('https://gist.githubusercontent.com/avi12/cd1d6728445608d64475809a8ddccc9c/raw/030974baed3eaadb26d9378979b83b1d30a265a3/url-input-example.txt')

  .then(response => response.text())

  .then(parse)

  .then(console.log);

.as-console-wrapper { max-height: 100% !important; top: 0; }



查看完整回答
反对 回复 2023-07-14
?
莫回无

TA贡献1865条经验 获得超7个赞

Node.js 附带了一个内置的“querystring”npm 包实用程序,但在这里我使用了一个更好的实用程序,称为“qs”。您可以在数组中指定分隔符,而不是仅对前者使用一个分隔符。


如果您想使用内置的“querystring”包,则需要在调用解析时删除分隔符数组,并检查字符串以查看使用的分隔符 - 您提供的示例文件使用了几个不同的分隔符。


所以试试这个:


const qs = require("qs");

let params = `foo=bar&foo1=foo%3Dbar%26foo2%3Dfoo%253Dbar`;


const isObject = (param) => {

  try {

    let testProp = JSON.parse(param);

    if (typeof testProp === "object" && testProp !== null) {

      return true;

    }

    return false;

  } catch (e) {

    return false;

  }

};


const isURL = (value) => {

  try {

    new URL(value);

  } catch (e) {

    return false;

  }

  return true;

};


const isQueryString = (value) => {

  if (/[/&=]/.test(value) && !isURL(value)) {

    return true;

  } else {

    return false;

  }

};


const parseData = (data, parsed = false) => {

  if (isQueryString(data) && !parsed) {

    return parseData(qs.parse(data, { delimiter: /[;,/&]/ }), true);

  } else if (isObject(data) || parsed) {

    for (let propertyName in data) {

      if (isObject(data[propertyName])) {

        data[propertyName] = parseData(JSON.parse(data[propertyName]), true);

      } else {

        data[propertyName] = parseData(data[propertyName]);

      }

    }


    return data;

  } else {

    return data;

  }

};

let s = parseData(params);

console.log(JSON.stringify(s, null, 2));


查看完整回答
反对 回复 2023-07-14
  • 3 回答
  • 0 关注
  • 153 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信