【问题标题】:Remove extra white spaces from the json从 json 中删除多余的空格
【发布时间】:2020-05-01 21:19:53
【问题描述】:

在 Java 中,我有一个 json 字符串,我想从中删除多余的空格。我不想从键和值中的字符中删除空格。

实际的 JSON 字符串

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

必需的 JSON

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

【问题讨论】:

  • 为什么?那有什么可能的用途?
  • 所以你想缩小它?
  • 我想将这个 json 与另一个 json 进行比较。但由于空间太大,我无法比较。
  • 无论如何,您不应该在字符串级别进行比较。将字符串作为 Java 对象读取,并将其与其他 Java 对象进行比较。
  • 解析两个 JSON 并比较它们。避免此类操作。

标签: java json parsing


【解决方案1】:

更简单更安全的解决方案是使用 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 Repository

【讨论】:

  • 请注意,此解决方案可能会稍微改变数据的内容。例如,具有空值的键似乎从输出中被过滤掉了。这可能会或可能不会被接受,但无论如何都应该在答案文本中指出!
【解决方案2】:

我会选择这样的:

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 中可以很容易地创建类似的东西,或者从数据库中读取或由用户键入。
  • @StephenP true,感谢您指出。调整答案
  • ? 将处理转义引用\" 的情况,但会打开另一罐蠕虫;解析并不容易。如果在字符串中遇到\t 会发生什么? \\t 或者像我使用的 \\\" 怎么样?当用反斜杠转义时,许多事情都有特殊含义,而不仅仅是“在反斜杠后面打印字符”——例如\t 应该变成制表符0x09。如果您有一组有限且已知的输入,您可以安全地使用您的方法,这可能是 OPs 的情况,但未来的读者可能会尝试将其推断为一般情况,这就是我指出的原因。
  • @StephenP 是的,应该为更多的读者指出,无论如何范围是替换空格。它不会以任何方式解析 \t ,但它也不会丢失,这意味着我猜最后的反序列化器应该仍然能够正确解析所有内容
【解决方案3】:

@Fabian Z 所说的可能会起作用,但可以进行优化(您不需要先将整个 String 转换为 char 数组来迭代它,您还应该使用 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)

它还会删除换行符

【讨论】:

  • 你有没有做过基准测试?我只是好奇,与枚举相比,使用 charAt(i) 是否/何时变得更昂贵。
  • 不,我没有对此进行基准测试,但是您将如何在字符串上使用枚举?我想即使是 JsonParser 也会一个接一个地读取字符。您可以做很多改进的方法是告诉 StringBuilder 它需要的容量。只需在 StringBuilder 的构造函数中传递 json.length() 即可。据我所知,这应该是一个非常有效的实现。你会改变什么来让它更快?
  • 正如@Andrei Kovrov 提到的,您还应该考虑转义引号。但是你应该在构造函数中明确地告诉 StringBuilder / StringBuffer json 的长度。
【解决方案4】:

不要忘记转义引号\"

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();
}

【讨论】:

    【解决方案5】:

    如果您使用 JsonWriter 来创建该 Json 代码,您可以这样做

    jsonWriter.setIndent("");
    

    删除 json 代码中的所有空格(使用 Gson 的 Json Writer 测试)

    【讨论】:

    • 我有一个 org.JSONObject,我正在将它转换为一个字符串。在转换时,我正在尝试删除多余的空格
    【解决方案6】:

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

    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。

    【讨论】: