如何让我的JSON更简洁?

时间:2011-05-25 21:18:05

标签: ajax string json serialization

我目前正在开发一个Web应用程序,并使用JSON进行ajax请求和响应。我有一个区域,我以一个超过10000个对象的数组的形式向客户端返回一个非常大的数据集。这是示例的一部分(在某种程度上已经简化):

"schedules": [
        {
            "codePractice": 35,
            "codeScheduleObject": 576,
            "codeScheduleObjectType": "",
            "defaultCodeScheduleObject": 12,
            "name": "Dr. 1"
        },
        {
            "codePractice": 35,
            "codeScheduleObject": 169,
            "codeScheduleObjectType": "",
            "defaultCodeScheduleObject": 43,
            "name": "Dr. 2"
        },
        {
            "codePractice": 35,
            "codeScheduleObject": 959,
            "codeScheduleObjectType": "",
            "defaultCodeScheduleObject": 76,
            "name": "Dr. 3"
        }
    ]

正如您可以想象的那样,对于此数组中的大量对象,JSON响应的大小可能非常大。

我的问题是,是否有一个JSON字符串/解析器可以将"schedules"数组转换为类似于JSON字符串的内容:

"schedules": [
    ["codePractice", "codeScheduleObject", "codeLogin", "codeScheduleObjectType", "defaultCodeScheduleObject","name"],
    [35, 576, "", 12, "Dr. 1"],
    [35, 169, "", 43, "Dr. 2"],
    [35, 959, "", 76, "Dr. 3"],
]

即,在"schedules"数组的开头会有一个数组,它包含该数组对象的键,所有其他容器数组都将保存这些值。

如果我愿意的话,我可以在服务器上进行转换并在客户端上解析它,但我想知道是否有用于解析/字符串化大型JSON的标准库?

我也可以通过缩小器来运行它,但是我想保留我目前的密钥,因为它们在应用程序中提供了一些上下文。

我也希望你可以在这里批评我的方法或建议替代方案?

5 个答案:

答案 0 :(得分:11)

HTTP压缩(即gzip或deflate)已经完全正确。重复的模式(如JSON密钥)将替换为标记,以便每次传输时只需要进行一次详细模式。

答案 1 :(得分:7)

不是答案,而是基于10k条目和一些虚假数据粗略估计“节省”:-)这是对我发布的评论的回应。增加的复杂性是否会使模式化方法值得?

“这取决于。”

这个C#是LINQPad,可以随时进行测试/修改:

string LongTemplate (int n1, int n2, int n3, string name) {
    return string.Format(@"
            {{
                ""codePractice"": {0},
                ""codeScheduleObject"": {1},
                ""codeScheduleObjectType"": """",
                ""defaultCodeScheduleObject"": {2},
                ""name"": ""Dr. {3}""
            }}," + "\n", n1, n2, n3, name);
}

string ShortTemplate (int n1, int n2, int n3, string name) {
    return string.Format("[{0}, {1}, \"\", {2}, \"Dr. {3}\"],\n",
        n1, n2, n3, name);
}

string MinTemplate (int n1, int n2, int n3, string name) {
    return string.Format("[{0},{1},\"\",{2},\"Dr. {3}\"],",
        n1, n2, n3, name);
}

long GZippedSize (string s) {
    var ms = new MemoryStream();
    using (var gzip = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true))
    using (var sw = new StreamWriter(gzip)) {
        sw.Write(s);
    }
    return ms.Position;
}

void Main()
{
    var r = new Random();
    var l = new StringBuilder();
    var s = new StringBuilder();
    var m = new StringBuilder();
    for (int i = 0; i < 10000; i++) {
        var n1 = r.Next(10000);
        var n2 = r.Next(10000);
        var n3 = r.Next(10000);

        var name = "bogus" + r.Next(50);
        l.Append(LongTemplate(n1, n2, n3, name));
        s.Append(ShortTemplate(n1, n2, n3, name));
        m.Append(MinTemplate(n1, n2, n3, name));
    }

    var lc = GZippedSize(l.ToString());
    var sc = GZippedSize(s.ToString());
    var mc = GZippedSize(s.ToString());
    Console.WriteLine(string.Format("Long:\tNormal={0}\tGZip={1}\tCompressed={2:P}", l.Length, lc, (float)lc / l.Length));
    Console.WriteLine(string.Format("Short:\tNormal={0}\tGZip={1}\tCompressed={2:P}", s.Length, sc, (float)sc / s.Length));
    Console.WriteLine(string.Format("Min:\tNormal={0}\tGZip={1}\tCompressed={2:P}", m.Length, mc, (float)mc / m.Length));
    Console.WriteLine(string.Format("Short/Long\tRegular={0:P}\tGZip={1:P}",
        (float)s.Length / l.Length, (float)sc / lc));
    Console.WriteLine(string.Format("Min/Long\tRegular={0:P}\tGZip={1:P}",
        (float)m.Length / l.Length, (float)mc / lc));
}

我的结果:

Long:  Normal=1754614  GZip=197053  Compressed=11.23 %
Short:  Normal=384614  GZip=128252  Compressed=33.35 %
Min:  Normal=334614  GZip=128252  Compressed=38.33 %
Short/Long  Regular=21.92 %  GZip=65.09 %
Min/Long  Regular=19.07 %  GZip=65.09 %

结论:

  • 最大的节省是使用GZIP(比使用schema'ize更好)。
  • GZIP + schema'ized将是最小的整体。
  • 使用GZIP, no point 使用普通的JavaScript最小化器(在此方案中)。
  • 使用GZIP(例如DEFLATE);它在重复的结构化文本上表现很好(正常情况下压缩率为900%)。

快乐的编码。

答案 2 :(得分:6)

这篇文章几乎完成了你想要做的事情:

http://stevehanov.ca/blog/index.php?id=104

乍一看,在算法的第一步之后,您的示例看起来会被压缩到以下内容,这将在后续步骤中对其进行更多的处理):

{
    "templates": [ 
        ["codePractice", "codeScheduleObject", "codeScheduleObjectType", "defaultCodeScheduleObject", "name"] 
    ],
    "values": [ 
        { "type": 1, "values": [ 35, 576, "", 12, "Dr. 1" ] },
        { "type": 1, "values": [ 35, 169, "", 43, "Dr. 2" ] },
        { "type": 1, "values": [ 35, 959, "", 76, "Dr. 3" ] }
    ]
}

您可以开始看到算法的好处。这是通过压缩器运行后的最终输出:

{
    "f" : "cjson",
    "t" : [
              [0,"schedules"],
              [0,"codePractice","codeScheduleObject","codeScheduleObjectType","defaultCodeScheduleObject","name"]
          ],
    "v" : {
        "" : [ 1, [
                { "" : [2, 35, 576, "", 12, "Dr. 1"] },
                { "" : [2, 35, 169, "", 43, "Dr. 2"] },
                { "" : [2, 35, 959, "", 76, "Dr. 3"] }
            ]
        ]
    }
}

如果您有数千条记录,显然可以看到改进。输出仍然是可读的,但我认为其他人也是对的:一个好的压缩算法将删除无论如何重复的文本块...

答案 3 :(得分:3)

在更改JSON架构之前,请先尝试一下

http://httpd.apache.org/docs/2.0/mod/mod_deflate.html

答案 4 :(得分:0)

为了记录,我正在用PHP完成它。它是数据库中的对象列表。

import {AbstractControl, ValidatorFn} from '@angular/forms';

export function confirmPasswordValidator(): ValidatorFn {
  return (control: AbstractControl) => {
    const pswd = control.value;
    const passwordConfirm = control.root.value.verification.password;
    return (pswd === passwordConfirm) ? null : {'strongPasswordError': {pswd}};
  };
}

json:string(22501 length)

gz compressed = string(711)但它是二进制格式。

gz compressed + base64 = string(948)是一种文本格式。

所以,通过使用一小部分秒,它相当小。