从json中移除多余的空格

5

我想在Java中去除JSON字符串中的额外空格,但不想移除键和值中的字符间的空格。

实际的JSON字符串:

{ "Error" : "Invalid HTTP Method" , "ErrorCode" : "405" , "ErrorDesc" : "Method Not Allowed" } 

所需的 JSON

{"Error":"Invalid HTTP Method","ErrorCode":"405","ErrorDesc":"Method Not Allowed"}

2
为什么呢?那有什么可能的用途呢? - Mike 'Pomax' Kamermans
所以你想要进行代码压缩吗? - Clashsoft
1
我想将这个JSON与另一个JSON进行比较。但是由于额外的空格,我无法进行比较。 - esahmo
2
无论如何,您不应该在字符串级别进行比较。将字符串作为Java对象读取,并将其与其他Java对象进行比较。 - DYZ
1
解析这两个JSON并进行比较。避免这样的操作。 - Harshal Parekh
无论如何,将json对象作为字符串进行比较都是注定失败的,因为json对象没有定义字段的顺序。{"a":1,"b":2}等同于{"b":2,"a":1} - Arfur Narf
6个回答

10

一个更简单而更安全的解决方案是使用 Gson库(只需要几行代码):

public static String simplify(String json) {
    Gson gson = new GsonBuilder().create();

    JsonElement el = JsonParser.parseString(json);
    return gson.toJson(el);
}

你甚至可以通过Gson的漂亮输出选项,反转整个过程(添加空格):

public static String beautify(String json) {
    Gson gson = new GsonBuilder().setPrettyPrinting().create();

    JsonElement el = JsonParser.parseString(json);
    return gson.toJson(el);
}

希望这能帮助你。 你可以在这里获取最新版本: Gson Maven 仓库

1
请注意,此解决方案可能会稍微更改数据内容。例如,具有空值的键似乎已从输出中过滤掉。这可能是可以接受的,也可能不可接受,但无论如何,在答案文本中都应该指出! - Lii

3

我会选择类似这样的东西:

public static void main(String[] args) {
    String json = "{ \"Error\": \"Inv\\\"alid HTTP Method\", \"ErrorCode\":\"405\",\"ErrorDesc\":\"Method Not Allowed\"}";

    System.out.println(removeWhitespaces(json));
}

public static String removeWhitespaces(String json) {

    boolean quoted = false;
    boolean escaped = false;
    String out = "";

    for(Character c : json.toCharArray()) {

        if(escaped) {
            out += c;
            escaped = false;
            continue;
        }

        if(c == '"') {
            quoted = !quoted;
        } else if(c == '\\') {
            escaped = true;
        }

        if(c == ' ' &! quoted) {
            continue;
        }

        out += c;

    }

    return out;

}

测试运行返回

{"Error":"Invalid HTTP Method","ErrorCode":"405","ErrorDesc":"Method Not Allowed"}

请小心自行解析字符串,因为如果字符串内嵌有引号标记,它将失败,这种情况是可能发生的。 在JS中,这将创建一个失败案例:let myjsonstr = '{"Error" : "Invalid Method", "ErrorCode" : 405, "ErrMsg" : "This \\"Thing\\" is not allowed" }';类似的事情在Java中、从数据库中读取或由用户输入时同样容易发生。 - Stephen P
@StephenP 确实,谢谢你指出来。已经调整了答案。 - Fabian Zimbalev
1
这会处理转义引号 \" 的情况,但也会导致其他一些问题;解析并不容易。如果在字符串中遇到 \t 会怎样?如果是 \\t 或者像我使用的 \\\" 又会怎样?很多字符在用反斜杠转义后都具有特殊的含义,而且不仅仅是“打印跟在反斜杠后面的字符”——比如,\t 应该被转换成制表符 0x09。如果你只有一个有限而已知的输入集合,那么你的方法可能是安全的,这也可能是 OPs 的情况,但未来的读者可能会试图将此推广到一般情况,这就是我指出这一点的原因。 - Stephen P
@StephenP 是的,应该为进一步的读者指出,无论如何,范围是替换空格。它不会以任何方式解析 \t,但也不会丢失,这意味着最终的反序列化器仍然应该能够正确解析所有内容,我想。 - Fabian Zimbalev

1

@Fabian Z说的可能可以用,但可以进行优化(你不需要先将整个字符串转换为字符数组来迭代它,而且你应该使用StringBuilder):

public static String removeWhitespaces(String json) {
    boolean quoted = false;

    StringBuilder builder = new StringBuilder();

    int len = json.length();
    for (int i = 0; i < len; i++) {
        char c = json.charAt(i);
        if (c == '\"')
            quoted = !quoted;

        if (quoted || !Character.isWhitespace(c))
            builder.append(c);
    }

    return builder.toString();
}

同时,当使用时

Character.isWhitespace(c)

它还会删除换行符。

1
你有对这个进行基准测试吗?我只是好奇,当使用charAt(i)相比枚举更昂贵时会发生什么。 - Fabian Zimbalev
我没有进行基准测试,但你会如何在字符串上使用枚举?我想即使是 JsonParser 也会逐个读取字符。你可以做的事情就是告诉 StringBuilder 它需要的容量,这样可以大大提高效率。只需在 StringBuilder 的构造函数中传递 json.length() 即可。据我所知,这应该是一个相当高效的实现。你会如何更改以使其更快? - IntoVoid
正如@Andrei Kovrov提到的那样,你还应该考虑转义引号。但是你肯定要在构造函数中告诉StringBuilder / StringBuffer关于json的长度。 - IntoVoid

1
不要忘记转义引号 \"
static String minimize(String input){
     StringBuffer strBuffer = new StringBuffer();    
     boolean qouteOpened = false;
     boolean wasEscaped = false;
     for(int i=0; i<input.length(); i++){
         char c = input.charAt(i);
         if (c == '\\') {
            wasEscaped = true;
         }
         if(c == '"') {
             qouteOpened = wasEscaped ? qouteOpened : !qouteOpened;
         }
         if(!qouteOpened && (c == ' ')){
             continue;
         }
         if (c != '\\') {
            wasEscaped = false;
         }
         strBuffer.append(c);
     }
     return strBuffer.toString();
}

0

好的,这可能是我对这篇文章的最终回答:

public static CharSequence removeWhitespaces(CharSequence json) {
    int len = json.length();

    StringBuilder builder = new StringBuilder(len);

    boolean escaped = false, quoted = false;
    for (int i = 0; i < len; i++) {
        char c = json.charAt(i);
        if (c == '\"') {
            if (!escaped) quoted = !quoted;
            else escaped = false;
        } else if (quoted && c == '\\') {
            escaped = true;
        }

        if (quoted || c != ' ') {
            builder.append(c);
        }
    }

    return builder;
}

如果你想要确保去除所有的空格字符,那么可以使用如下代码:

public static CharSequence removeWhitespaces(CharSequence json) {
    int len = json.length();

    StringBuilder builder = new StringBuilder(len);

    boolean escaped = false, quoted = false;
    for (int i = 0; i < len; i++) {
        char c = json.charAt(i);
        if (c == '\"') {
            if (!escaped) quoted = !quoted;
            else escaped = false;
        } else if (quoted && c == '\\') {
            escaped = true;
        }

        if (quoted || !Character.isWhitespace(c)) {
            builder.append(c);
        }
    }

    return builder;
}

这种方法比先将字符串转换为Json结构,再转回字符串要高效得多,因为那样会非常耗时。

如果你有一个很长的输入字符串,提前告诉StringBuilder应该具有的起始容量也可以大大加快处理速度。 (容量不等于长度,这意味着即使你告诉StringBuilder它应该具有100的容量,它仍然只有你放入其中的文本长度)

由于StringBuilder实现了CharSequence,因此您可以直接返回整个StringBuilder,而无需将其转换回String。但是,如果您需要一个String而不是CharSequence,请在此方法的末尾调用builder.toString()并将返回类型设置为String。


0
如果您正在使用JsonWriter来创建该Json代码,您可以执行以下操作:
jsonWriter.setIndent("");

去除 JSON 代码中的所有空格(已使用 Gson 的 Json Writer 进行测试)


我有一个 org.JSONObject 对象,我正在将其转换为字符串。在转换的过程中,我试图去除额外的空格。 - esahmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接