如果您需要稍微修改原始文件的内容,我建议您编写一个自定义包装器来实现 java.io.Reader/java.io.InputStream 并将此阅读器/流传递给您的解析库。这个包装器会即时修改内容。例如:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
public class JsonFixer {
private static final String CORRUPTED_JSON = "{\n" +
" \"name\": \"Amy Brown\",\n" +
" \"value\": 123,\n" +
" \"comment\": \"He just exclaimed \"OMG\" when I approached him\",\n" +
" \"comment\": \"He just exclaimed \\\"OMG\" when I approached him\",\n" +
" \"comment\": \"He just exclaimed \\\"OMG\\\" when I approached him\"\n" +
"}";
public static class FixingReader extends Reader {
private final StringBuilder fixedLine = new StringBuilder();
private final BufferedReader lineReader;
private char[] currentLine;
private int currentLineStart;
private int currentLineLength;
public FixingReader(final Reader reader) {
if (reader instanceof BufferedReader) {
lineReader = (BufferedReader) reader;
} else {
lineReader = new BufferedReader(reader);
}
}
@Override
public int read(final char[] cbuf, final int off, final int len) throws IOException {
if (currentLineLength > 0) { // make the read of the rest of line
final int left = currentLineLength - currentLineStart;
final int read = Math.min(len, left);
System.arraycopy(currentLine, currentLineStart, cbuf, off, read);
currentLineStart += read;
if (currentLineStart == currentLineLength) {
currentLineStart = 0;
currentLineLength = 0;
}
return read;
}
final String line = lineReader.readLine();
if (line == null) { // EOF
currentLineStart = 0;
currentLineLength = 0;
return -1;
}
int lineLength = line.length() + 1; // including \n on the end of the line to be restored
currentLine = currentLine == null || currentLine.length < lineLength ?
new char[lineLength] :
currentLine; // reuse if we have enough space
line.getChars(0, line.length(), currentLine, 0);
currentLine[lineLength - 1] = '\n';
fixedLine.setLength(0);
// find the opening quotation mark
int openQuoteIdx = -1;
int qtCnt = 0;
for (int i = 0; i < lineLength; i++) {
final char c = currentLine[i];
fixedLine.append(c); // write start of the line
if (c != '"') {
continue;
}
qtCnt++;
if (qtCnt == 3) {
openQuoteIdx = i;
break;
}
}
// find the closing quotation mark
int closeQuoteIdx = -1;
for (int i = lineLength - 1; i > 0; i--) {
if (currentLine[i] != '"') {
continue;
}
closeQuoteIdx = i;
break;
}
if (openQuoteIdx > -1) { // if the line has quotation marks for the value
// copy the rest of the string replacing the quotation mark
boolean wasQuoted = false;
for (int i = openQuoteIdx + 1; i < lineLength; i++) {
final char c = currentLine[i];
if (i >= closeQuoteIdx) {
fixedLine.append(c); // write end of the line
continue;
}
// can see a quotation mark
switch (c) {
case '\\':
wasQuoted = true;
break;
case '"':
if (!wasQuoted) {
fixedLine.append('\\');
}
default:
wasQuoted = false;
}
fixedLine.append(c);
}
if (fixedLine.length() > lineLength) {
currentLine = new char[fixedLine.length()];
fixedLine.getChars(0, fixedLine.length(), currentLine, 0);
lineLength = currentLine.length;
}
}
currentLineStart = 0;
currentLineLength = lineLength;
// make the read
final int read = Math.min(len, currentLineLength);
System.arraycopy(currentLine, currentLineStart, cbuf, off, read);
currentLineStart += read;
if (currentLineStart == currentLineLength) {
currentLineLength = 0;
}
return read;
}
@Override
public void close() throws IOException {
lineReader.close();
}
}
public static void main(String[] args) throws Exception {
try (BufferedReader fixedJson = new BufferedReader(new FixingReader(new StringReader(CORRUPTED_JSON)))) {
fixedJson.lines().forEach(System.out::println);
}
}
}
打印以下输出:
{
"name": "Amy Brown",
"value": 123,
"comment": "He just exclaimed \"OMG\" when I approached him",
"comment": "He just exclaimed \"OMG\" when I approached him",
"comment": "He just exclaimed \"OMG\" when I approached him"
}
这种轻量级的方法甚至允许您转换大文件,因为它一次只需要存储一行。此特定实现仅适用于一行包含不超过一个对象属性及其值的情况。否则,您必须正确修改解析。