将JSON文件保存为UTF-8编码

3

我正在编写一个将一些JSON写入文件的方法,这个方法可以正常工作。然而,尽管我已经设置了输出为UTF-8,Oxygen 无法读取英镑符号和欧元符号。

Java代码:

Path logFile = Paths.get(this.output_folder + "/" + file.getName().split("\\.")[0] + ".json");
try (BufferedWriter writer = Files.newBufferedWriter(logFile, StandardCharsets.UTF_8)) {
    File fileDir = new File("test.json");
    Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileDir), "UTF8"));
    ObjectMapper mapper = new ObjectMapper();
    writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(all_questions));
}

"

“all_questions”是一个Question对象的ArrayList,它正在被ObjectMapper格式化为JSON。以下是一些带有井号的示例JSON:

"
{
      "name" : "RegExRule",
      "field" : "Q039_4",
      "rules" : [ ],
      "fileName" : "s1rules_england_en.xml",
      "error" : null,
      "pattern_match" : {
        "$record.ApplicationData.SiteVisit.VisitContactDetails.ContactOther.PersonName.PersonGivenName" : "^[\\u0000-\\u005F\\u0061-\\u007B\\u007d-\\u007f£€]*$"
      }
}

然而,在记事本++中显示,但在Oxygen中显示如下:
"pattern_match" : {
        "$record.ApplicationData.SiteVisit.VisitContactDetails.ContactOther.PersonName.PersonGivenName" : "^[\\u0000-\\u005F\\u0061-\\u007B\\u007d-\\u007f£€]*$"
 }

Notepad++ 的编码设置为“UTF-8”并且显示正确吗? - Binkan Salaryman
是的。我预计在oXygen中不会这样,但我希望能够告诉任何编辑器如何从文件内容本身打开文件? - Jon
如果问题与JSON/文件本身无关,您应该在问题中添加一个“Oxygen”标签。- Oxygen是否支持UTF-8? - Binkan Salaryman
我找不到标签。我找到了“Oxygene”... - Jon
文件没有UTF8 BOM标记,请尝试在开头添加魔术字节0xEF、0xBB、0xBF,看看Oxygen如何读取它。Notepad++有UTF8和UTF8WithoutBOM格式,而Notepad总是保存UTF8withBOM。 - Whome
1个回答

5
构造OutputStreamWriter对象时,需要使用"UTF-8"作为字符集名称,而不是"UTF8"
new OutputStreamWriter(..., "UTF-8")

或者,使用StandardCharsets.UTF_8

new OutputStreamWriter(..., StandardCharsets.UTF_8)

Java通常不支持读写BOM,因此如果您希望JSON文件具有UTF-8 BOM,则需要手动编写:

Writer out = ...;
out.write("\uFEFF");
out.write(... json content here ...); 

请注意,PrintWriter 可以为您管理 OutputStreamWriterFileOutputStream 对象:

Writer out = new PrintWriter(fileDir, "UTF-8");

或者:

Writer out = new PrintWriter("test.json", "UTF-8");

最后,你为什么要使用Files.newBufferedWriter()创建一个BufferedWriter却忽略它并手动创建另一个BufferedWriter呢?为什么不直接使用已有的BufferedWriter呢?
Path logFile = Paths.get(this.output_folder + "/" + file.getName().split("\\.")[0] + ".json");
try (BufferedWriter writer = Files.newBufferedWriter(logFile, StandardCharsets.UTF_8)) {
    writer.write("\uFEFF");
    ObjectMapper mapper = new ObjectMapper();
    writer.write(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(all_questions));
}

BOM标记字节为UTF-8=EF,BB,BF / UTF-16BigEndian=FEFF / UTF16LittleEndian=FFFE - Whome
工作得很好,谢谢。我觉得我的代码里有一些尝试的混合物,抱歉弄乱了。另外,我尝试了“UTF8”和“UTF-8”,但没有尝试“UTF_8”!唉呀。 - Jon
1
@Whome:是的,编码BOM的二进制字节是0xEF 0xBB 0xBF(UTF-8),0xFE 0xFF(UTF-16LE)和0xFF 0xFE(UTF-16BE),但BOM本身始终是Unicode代码点U+FEFF,因此您必须将该字符传递给Writer.write(),以便在写入输出流时由指定的字符集进行编码('\uFEFF' -> 0xEF 0xBB 0xBF用于UTF-8)。 - Remy Lebeau
@Jon:在通过名称字符串指定字符集时,您必须使用"UTF-8",没有"UTF8""UTF_8"字符集名称。UTF_8StandardCharsets类的静态Charset字段。 - Remy Lebeau
@RemyLebeau:经过十年的Java编程,我不知道Writer IO可以决定写入底层outputstream的最终bom标记字节。我一直使用FileOutputStream引用来写入前导bom字节。天啊。 - Whome
@Whome:Java没有BOM的概念。请记住,Java字符串/字符只是UTF-16编码中的Unicode代码点。该代码传递了一个普通的Java字符作为Unicode代码点U+FEFF ZERO WIDTH NON BREAKING SPACE(也用于BOM),并让Writer使用其指定的字符集对其进行编码,就像对任何其他输入字符/字符串一样。Writer不知道正在写入BOM。在UTF-8中,U+FEFF被编码为0xEF 0xBB 0xBF。在文件中间/结尾调用writer.write('\uFEFF'),您将看到相同的字节出现。 - Remy Lebeau

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