【问题标题】:Recursively decode JSON string until the decoded string is a valid JSON [duplicate]递归解码JSON字符串,直到解码的字符串是有效的JSON [重复]
【发布时间】:2018-05-13 09:10:55
【问题描述】:

我有一个一次性 URL 编码字符串:

$encodedJson = "%5B%7B%0A%09%22base%22%3A%20%7B%0A%09%09%22url%22%3A%20%22abc.com%22%2C%0A%09%09%22referrer%22%3A%20%22xyz.com%22%0A%09%7D%0A%7D%2C%20%7B%0A%09%22client%22%3A%20%7B%0A%09%09%22Pixel%22%3A%20false%2C%0A%09%09%22screen%22%3A%20%221680x1050%22%0A%09%7D%0A%7D%5D"

如果我使用以下函数,我有一个解码的 JSON,它是一个数组:

$decodedJsonArray = json_decode(rawurldecode($encodedJson), true);

然后print_r($decodedJsonArray); 给了我想要的输出:

Array
(
    [0] => Array
        (
            [base] => Array
                (
                    [url] => abc.com
                    [referrer] => xyz.com
                )

        )

    [1] => Array
        (
            [client] => Array
                (
                    [Pixel] => 
                    [screen] => 1680x1050
                )

        )

)

现在,假设我有一个多次 URL 编码的字符串:

$encodedJson = "%25255B%25257B%25250A%252509%252522base%252522%25253A%252520%25257B%25250A%252509%252509%252522url%252522%25253A%252520%252522abc.com%252522%25252C%25250A%252509%252509%252522referrer%252522%25253A%252520%252522xyz.com%252522%25250A%252509%25257D%25250A%25257D%25252C%252520%25257B%25250A%252509%252522client%252522%25253A%252520%25257B%25250A%252509%252509%252522Pixel%252522%25253A%252520false%25252C%25250A%252509%252509%252522screen%252522%25253A%252520%2525221680x1050%252522%25250A%252509%25257D%25250A%25257D%25255D"

此字符串经过三倍 URL 编码。现在,我想实现与以前相同的 JSON 数组。我正在尝试编写类似于以下的函数:

function recursiveJsonDecode($encodedJson) {
    if (isJson($encodedJson)) {
        return $encodedJson;
    } else {
        $decodedJsonArray = json_decode(rawurldecode($encodedJson), true);
        return $decodedJsonArray;
    }
}

但它不起作用。任何帮助将不胜感激。

【问题讨论】:

  • 在第二个示例中究竟是什么不起作用?向我们展示你得到的输出。
  • 我不知道PHP有一个函数叫isJson
  • 递归函数的一个非常重要的特性是它会调用自己。
  • @DontPanic 一个不称为聋哑递归函数的函数
  • 这只是一个理论练习,我猜?因为在现实中,您不应该处理不止一次编码的数据......这宁愿成为拒绝数据恕我直言的理由,并告诉谁将其发送给你要按顺序得到他们的东西...

标签: php json recursion urlencode urldecode


【解决方案1】:

Shipping & Post Office Supplies | USPS.com - Postal Store

订购运输用品很可爱,因为这是您唯一一次可以收到一盒只有盒子的盒子!

当您收到邮寄的盒子时,您会如何处理它们?我只取下最外面的包装,把我的盒子放在架子上;也许我以后会用它们把东西寄出去。编写递归 JSON 解码器的人可能会做一些不同的事情——他们可能会尝试打开所有这些盒子,然后发现他们什么也没收到而难过!

“我打开了每一个盒子,但我从来没有找到我的订单内容!”递归 JSON 解码器感叹


不要仅仅因为你可以解码它

无法确定字符串是否经过 JSON 编码。因此,决定是否解析不是消费者的工作。

以 JSON 字符串 "5" 为例,它是 '5' 的单一编码字符串吗?

json_encode("5");
// => '"5"'

还是双编码的整数 5

json_encode(json_encode(5));
// => '"5"'

如果您只查看 JSON 编码的结果,则无法判断,但 5 (int) 和 "5" (string) 与 [5]{value: 5} 一样不同——它们'是完全不同的类型——JSON 消费者必须知道值被编码了多少次。这并不复杂,因为您首先应该避免双重编码。


当我们解码 JSON 时,我们只做一次

json_decode('"5"');
// => "5"

你的递归函数可以有效地做到这一点

json_decode(json_decode('"5"'));
// => 5

其中只有 一个 是有效的答案——这就是为什么你会看到所有 isJson 函数都是围绕错误检查解码操作构建的——人们欺骗自己认为仅仅因为你可以解码字符串,它首先是 JSON。

回到我们的 USPS 示例,这意味着您只有在遇到无法打开的东西时才停止打开盒子 - 我只是继续打开盒子,一旦我发现它们都是空的,我就想知道我的订单在哪里内容是。

您可以通过某种方式检测 何时 以停止解码的想法从一开始就被打破了 - 在这个例子中,看看当我有一个简单的表单提交和一个用于处理的递归 JSON 解码器时会发生什么提交...

如果我用我的名字"[]" 填写表格,现在你对提交的表格数据使用递归 JSON 解码器,你最终会得到 ​​p>

$formData == [ "name" => [] ] // name is an array, wups!

而非递归 JSON 解码器会将名称保留为字符串

$formData == [ "name" => "[]" ] // name is a string, as the user typed

仅仅因为你可以解析它,并不意味着你应该


一旦字符串被双重或三重编码(URL 编码、JSON 编码或 任何 编码)- 反转它的唯一方法是将其解码完全相同的次数

【讨论】:

  • 那为什么他的代码示例都是php,而且他的问题上还有一个php标签?
  • 这个问题很模糊。我认为它是关于 url 编码,而不是专门关于 JSON。
  • 问题在 imo 中并不模糊——这个人是如何递归解码 JSON 并通过他们的尝试展示一个函数
  • @naomik 我认为您的答案的第一行是错误的,请参阅此 SO 帖子了解如何检测 JSON 数据:stackoverflow.com/q/6041741/3088508
  • David,字符串可以被 JSON 解码并不意味着它是 JSON
【解决方案2】:

您可以将 URL 解码视为定点操作:

function fixedPointDecode($string) {
     $decoded = urldecode($string); 
     while ($decoded != $string) {
         $string = $decoded;
         $decoded = urldecode($string);    
     }
     return $decoded;
}

这个想法是,如果urldecode 的结果没有改变原始字符串,那么它就会被完全解码。

那么你可以这样做:

 json_decode(fixedPointDecode($string));

注意:我没有发现任何迹象表明存在任何不收敛到固定点的 URL 编码字符串,但我很好奇其他人是否有。

【讨论】:

  • 唯一的问题是有时您会遇到一个有效值,该值也似乎被编码。假设我的设备 ID 为“frt%235”——即 实际 设备 ID——如果我让程序决定何时停止解码,它会将其更改为 frt#5,这在本案
  • 如果您将 while 更改为 while (json_decode($decoded) == null),那么这可能会起作用,但在这种情况下,如果输入中没有隐藏有效的 json 字符串,该函数将导致无限循环。我建议您通过确保字符串只有一次 urlencoded 来从源头解决问题
  • 但这就是重点,消费者无法控制数据的内容——依赖空/错误检查作为是否应该解析字符串的信号是不正确的
  • 嗯,这部分是正确的,但这也是您选择有缺陷的提供者的结果。获取多次 urlencoded 的字符串应该向提供者报告错误,并且提供者不应声称它按预期工作。在任何情况下,对整个字符串进行双重编码都不应该有任何意义。
  • @naomik 如果您提前知道它被编码了多少次,那么这绝对是比定点操作更好的策略。
【解决方案3】:

json_decode 将返回 null,如果它不是有效的 JSON,正如它所说的 here

如果无法解码 json 或编码数据深度超过递归限制,则返回 NULL。

所以只需测试一下:

while(($decodedJsonArray = json_decode($encodedJson, true)) === null) {
    $encodedJson = rawurldecode($encodedJson);
}

print_r($decodedJsonArray);

要使用您的isJson 功能:

while(!isJson($encodedJson)) {
    $encodedJson = rawurldecode($encodedJson);
}
$decodedJsonArray = json_decode($encodedJson, true);

print_r($decodedJsonArray);

【讨论】:

  • 哇,这比我的递归函数干净多了。投赞成票!
  • 随机投反对票 :(
  • 嗨,正如我对大卫的回答所评论的那样,如果我有一个像 $encodedJson = "%25etcetc"; 这样的字符串,你的函数也可以工作但是当我从文件中解析字符串时它不起作用,即 $encodedJson = file_get_contents("test.txt"); 或 @987654330 @ .知道为什么吗?
  • AbraCadavar,您刚刚从 USPS.com 订购了 1 箱 Priority Mail Shoe Box - 您收到订单后会打开多少箱?
【解决方案4】:

调用rawurldecode(rawurldecode(rawurldecode($encodedJson))) 显示您的字符串实际上是rawurldecoded 3 次,而不是json_encoded 3 次,所以我在每次迭代中都创建了递归函数rawurldecode,直到json_decode 起作用:

$encodedJson = "%25255B%25257B%25250A%252509%252522base%252522%25253A%252520%25257B%25250A%252509%252509%252522url%252522%25253A%252520%252522abc.com%252522%25252C%25250A%252509%252509%252522referrer%252522%25253A%252520%252522xyz.com%252522%25250A%252509%25257D%25250A%25257D%25252C%252520%25257B%25250A%252509%252522client%252522%25253A%252520%25257B%25250A%252509%252509%252522Pixel%252522%25253A%252520false%25252C%25250A%252509%252509%252522screen%252522%25253A%252520%2525221680x1050%252522%25250A%252509%25257D%25250A%25257D%25255D";

function recursiveJsonDecode ($inJson) {
    $outputArr = json_decode($inJson);
    if (json_last_error() == JSON_ERROR_NONE) {
        return $outputArr;
    } else {
        return recursiveJsonDecode(rawurldecode($inJson));
    }
}

print_r(recursiveJsonDecode($encodedJson));

eval.in demo

【讨论】:

  • 这在应用于任何通用基准时都不起作用
  • OP 的输入数据的方式,我想说它会起作用,如果它以与他的输入数组相同的方式搞砸了(首先是json_encoded,然后是rawurlencode d)。见这里:eval.in/910031
  • 嗨,如果我有一个像 $encodedJson = "%25etcetc"; 这样的字符串,你的函数就可以工作但是当我从文件中解析字符串时它不起作用,即 $encodedJson = file_get_contents("test.txt");$encodedJson = file_get_contents("test.json"); 。知道为什么吗?
  • @IqbalNazir 当我从数组中获取$encodedJson 时,它对我有用,但是因为我实际上并不知道您的test.txt 文件中有什么,所以我只是假设它是您发布的内容你的答案。请参阅此 eval.in:eval.in/910041 如果我的假设不正确,请将您的 test.txt 文件中的内容发布到 www.pastebin.com 等服务。
  • 谢谢伙计。它实际上正在工作。我的文本文件中有“”符号。删除它们后,它就可以工作了。
猜你喜欢
  • 2019-04-03
  • 2013-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 2011-08-27
  • 2011-02-02
  • 1970-01-01
相关资源
最近更新 更多