Gson Unicode字符转换为Unicode字符编码

3

请查看下面的代码。我有一个包含Unicode字符编码的JSON字符串。我将其转换为Java对象,然后再将其转换回JSON字符串。但是,您可以看到输入和输出的JSON字符串不匹配。使用Gson是否可能将我的对象转换为原始JSON字符串?我希望outputJsoninputJson相同。

static class Book {
    String description;
}

public static void test() {
    Gson gson = new Gson();

    String inputJson = "{\"description\":\"Tikrovi\\u0161kai para\\u0161ytas k\\u016brinys\"}";
    Book book = gson.fromJson(inputJson, Book.class);
    String outputJson = gson.toJson(book);

    System.out.println(inputJson);
    System.out.println(outputJson);
    // Prints:
    // {"description":"Tikrovi\u0161kai para\u0161ytas k\u016brinys"}
    // {"description":"Tikroviškai parašytas kūrinys"}
}

“原始的”是指将非ASCII字符编码为Unicode转义符,而不是混合输入字符串,该字符串可以包含Unicode字符和Unicode转义符? - Lyubomyr Shaydariv
我想让outputJson与inputJson匹配。问题在于Gson无法将's'转换为\u0161,'ū' >> \u016b. - Egis
3个回答

1

很不幸,Gson似乎不支持此功能。所有JSON输入/输出都集中在Gson中(从2.8.0开始)的JsonReaderJsonWriter中。 JsonReader可以使用其私有的readEscapeCharacter方法读取Unicode转义。然而,与JsonReader不同,JsonWriter仅将字符串写入备份Writer实例,对于大于127的字符不进行任何字符校正,除了\u2028\u2029。这里唯一能做的事情可能是编写一个自定义的转义Writer,以便您可以发出Unicode转义。

final class EscapedWriter
        extends Writer {

    private static final char[] hex = {
            '0', '1', '2', '3',
            '4', '5', '6', '7',
            '8', '9', 'a', 'b',
            'c', 'd', 'e', 'f'
    };

    private final Writer writer;

    // I/O components are usually implemented in not thread-safe manner
    // so we can save some time on constructing a single UTF-16 escape
    private final char[] escape = { '\\', 'u', 0, 0, 0, 0 };

    EscapedWriter(final Writer writer) {
        this.writer = writer;
    }

    // This implementation is not very efficient and is open for enhancements:
    // * constructing a single "normalized" buffer character array so that it could be passed to the downstream writer
    //   rather than writing characters one by one
    // * etc...
    @Override
    public void write(final char[] buffer, final int offset, final int length)
            throws IOException {
        for ( int i = offset; i < length; i++ ) {
            final int ch = buffer[i];
            if ( ch < 128 ) {
                writer.write(ch);
            } else {
                escape[2] = hex[(ch & 0xF000) >> 12];
                escape[3] = hex[(ch & 0x0F00) >> 8];
                escape[4] = hex[(ch & 0x00F0) >> 4];
                escape[5] = hex[ch & 0x000F];
                writer.write(escape);
            }
        }
    }

    @Override
    public void flush()
            throws IOException {
        writer.flush();
    }

    @Override
    public void close()
            throws IOException {
        writer.close();
    }

    // Some java.io.Writer subclasses may use java.lang.Object.toString() to materialize their accumulated state by design
    // so it has to be overridden and forwarded as well
    @Override
    public String toString() {
        return writer.toString();
    }

}

这个编写者没有经过充分测试,也不支持 \u2028\u2029。在调用 toJson 方法时只需配置输出目标即可:
final String input = "{\"description\":\"Tikrovi\\u0161kai para\\u0161ytas k\\u016brinys\"}";
final Book book = gson.fromJson(input, Book.class);
final Writer output = new EscapedWriter(new StringWriter());
gson.toJson(book, output);
System.out.println(input);
System.out.println(output);

输出:

{"description":"Tikroviškai parašytas kūrinys"}
{"description":"Tikroviškai parašytas kūrinys"}

这是一个有趣的问题,您可能还可以在google/gson上提出一个字符串写入配置选项的问题 - 或者至少从开发团队那里得到一些评论。我相信他们非常清楚这样的行为,并通过设计使其正常工作,但他们也可以解释一下(我现在能想到的唯一一个是,目前他们有一些性能,不需要在写入字符串之前进行额外的转换,但这只是一个猜测)。


0

有一个问题被标记为与此重复:unicode characters in json file to be unconverted after managing java gson [duplicate]。我回答了那个问题,并且我的答案被接受为适当的解决方案。因此,以下是我的答案副本:

实际上,Unicode 字符的一个很大的优点是,任何客户端都可以读取和处理代码 "\u...",就像它的字符表示一样。例如,在 HTML 文件中,如果您用其 Unicode 表示替换每个单独的字符,浏览器将像往常一样读取它。即用 '\u0048'(代表 'H' 的 Unicode)替换 "Hello world" 中的 'H',在浏览器中仍然会看到 "Hello world"。但在这种情况下,Gson 只是简单地用它们的符号替换 Unicode。

我的建议可能不完美,但它可以解决问题。在转换对象之前,请记住您的 Unicode 符号的位置,并在转换后将它们改回 Unicode。这里有一个工具可能会对您有所帮助:有一个由我编写的开源库 MgntUtils,其中包含一个将任何字符串转换为 Unicode 序列和反之的实用程序。

您可以这样做:

String s = StringUnicodeEncoderDecoder.encodeStringToUnicodeSequence("Hello world");

它会给你一个字符串:"\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u006f\u0072\u006c\u0064",然后你可以这样做:

    String s 
= StringUnicodeEncoderDecoder.decodeUnicodeSequenceToString("\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u006f\u0072\u006c\u0064");

它将返回字符串“Hello world”。它适用于任何语言。以下是解释如何获取库的文章链接:具有堆栈跟踪过滤器、静默字符串解析Unicode转换器和版本比较的开源Java库。查找标题为“字符串Unicode转换器”的段落。

这里是Maven构件的链接,这里是包含源代码和javadoc的Github链接。这里是javadoc


0
你可以在输入流上调用String.replace("\\", "\\\\"),欺骗Gson使其不将转义字符解释为此类字符。然后在输出字符串上调用String.replace("\\\\", "\\")来撤销这个操作。

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