假设我有一个Ruby Hash,它有一个非常大的字符串属性.它是如此之大,以至于压缩绳子可能是有意义的.使用ActiveSupport::Gzip.compress压缩字符串很简单,但是由于编码的原因,将散列转换为JSON被证明是一个问题.

基本上,此代码失败:

{ compressed: ActiveSupport::Gzip.compress('asdf') }.to_json

出现以下错误:

JSON::GeneratorError: Invalid Unicode [8b 08 00 56 dd] at 1

A hash that does not contain any compressed data gets encoded as UTF-8 when converted to json using to_json, but calling ActiveSupport::Gzip.compress('asdf').encode('UTF-8') just fails 出现以下错误:

Encoding::UndefinedConversionError: "\x8B" from ASCII-8BIT to UTF-8

我这是在做傻事吗?我的目标能实现吗?

推荐答案

Base-64 encode gzip输出,以使其可读字符,从而使其有效的UTF-8.这将使数据扩展约三分之一,抵消了您的一些压缩.您还可以使用更有效的字符进行编码,例如Base-85,以减少影响,在这种情况下扩展约四分之一.通过一些工作,你应该能够把它降低到接近七分之一的涨幅.

下面是用C语言编写的示例代码,它将字节编码为1..127中的符号,这些符号都是有效的UTF-8.(JSON不允许字符串中有空字节.)由此产生的扩展约为1.145倍.

#include <stddef.h>

// Encode, reading six or seven bits from bin[0..len-1] to encode each symbol
// to *enc, where a symbol is one byte in the range 1..127. enc must have room
// for at least ceil((len * 4) / 3) symbols. The average number of encoded
// symbols for random input is 1.1454 * len. The number of encoded symbols is
// returned.
size_t enc127(char *enc, unsigned char const *bin, size_t len) {
    unsigned buf = 0;
    int bits = 0;
    size_t i = 0, k = 0;
    for (;;) {
        if (bits < 7) {
            if (i == len)
                break;
            buf = (buf << 8) | bin[i++];
            bits += 8;
        }
        unsigned sym = ((buf >> (bits - 7)) & 0x7f) + 1;
        if (sym > 0x7e) {
            enc[k++] = 0x7f;
            bits -= 6;
        }
        else {
            enc[k++] = sym;
            bits -= 7;
        }
    }
    if (bits)
        enc[k++] = ((buf << (7 - bits)) & 0x7f) + 1;
    return k;
}

// Decode, converting each symbol from enc, which must be in the range 1..127,
// into 6 or 7 bits in the output, from which 8 bits at a time is written to
// bin. bin must have room for at least floor((len * 7) / 8) bytes. The number
// of decoded bytes is returned.
size_t dec127(unsigned char *bin, char const *enc, size_t len) {
    unsigned buf = 0;
    int bits = 0;
    size_t k = 0;
    for (size_t i = 0; i < len; i++) {
        unsigned sym = enc[i];
        if (sym == 0x7f) {
            buf = (buf << 6) | 0x3f;
            bits += 6;
        }
        else {
            buf = (buf << 7) | (sym - 1);
            bits += 7;
        }
        if (bits >= 8) {
            bin[k++] = buf >> (bits - 8);
            bits -= 8;
        }
    }
    return k;
}

Json相关问答推荐

如何将加权边列表导出到JSON树?

当列为空时从 SQL 获取 JSON

如何从 json 中获取单个元素?

JSON 字段的多个名称

使用 KQL 和外部 data() 运算符从 json 文件中提取信息

如何在 Dart 中与多个 map (字典)相交

如何将动态复杂 json 解析为dart 对象或模型

Golang / Go - 如果 struct 没有字段,如何将其编组为空?

如何从 rails 中的 respond_to 方法生成 json?

在 JSON 字符串中react i18n 换行符

如何从Typescript 中的json响应中获取日期对象

在 Rails 中使用 JSON 创建嵌套对象

如何通过 NSJSONSerialization 在 JSON 中包含空值?

字符串的 Gson 数组到 JsonArray

如何在java中比较来自JsonObject的空值

JSON 到 JSON 转换器

Json.NET 是否缓存类型的序列化信息?

与classic 规范化表相比,postgres JSON 索引是否足够高效?

有没有办法折叠 Postman 中的所有 json 字段

Newtonsoft 对象 → 获取 JSON 字符串