Классы
Вот два класса, которые решают начальную проблему со сломанным json:
public class FixedJson {
private final String target;
private final Pattern pattern;
public FixedJson(String target) {
this(
target,
Pattern.compile("\"(.+?)\"[^\\w\"]")
);
}
public FixedJson(String target, Pattern pattern) {
this.target = target;
this.pattern = pattern;
}
public String value() {
return this.pattern.matcher(this.target).replaceAll(
matchResult -> {
StringBuilder sb = new StringBuilder();
sb.append(
matchResult.group(),
0,
matchResult.start(1) - matchResult.start(0)
);
sb.append(
new Escaped(
new Escaped(matchResult.group(1)).value()
).value()
);
sb.append(
matchResult.group().substring(
matchResult.group().length() - (matchResult.end(0) - matchResult.end(1))
)
);
return sb.toString();
}
);
}
}
public class Escaped {
private final String target;
private final Pattern pattern;
public Escaped(String target) {
this(
target,
Pattern.compile("[\\\\\"]")
);
}
public Escaped(String target, Pattern pattern) {
this.target = target;
this.pattern = pattern;
}
public String value() {
return this.pattern
.matcher(this.target)
.replaceAll("\\\\$0");
}
}
Модульные тесты
И я написал unit-тесты для подтверждения правильности:
import java.util.regex.Pattern;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class FixedJsonTest {
@Test
public void normalValue() {
assertEquals(
"{\"notes_text\": \"someString\"}",
new FixedJson("{\"notes_text\": \"someString\"}").value()
);
}
@Test
public void valueWithOneDoubleQuotes() {
assertEquals(
"{\"notes_text\": \"\\\"\"}",
new FixedJson("{\"notes_text\": \"\"\"}").value()
);
}
@Test
public void valueWithOneDoubleQuotesAndAnotherValue() {
assertEquals(
"{\"notes_text\": \"\\\"\", \"hello\": \"world\"}",
new FixedJson("{\"notes_text\": \"\"\", \"hello\": \"world\"}").value()
);
}
@Test
public void valueWithTwoDoubleQuotes() {
assertEquals(
"{\"notes_text\": \"\\\"\\\"\"}",
new FixedJson("{\"notes_text\": \"\"\"\"}").value()
);
}
@Test
public void valueWithTwoDoubleQuotesAndAnotherValue() {
assertEquals(
"{\"notes_text\": \"\\\"\\\"\", \"hello\": \"world\"}",
new FixedJson("{\"notes_text\": \"\"\"\", \"hello\": \"world\"}").value()
);
}
@Test
public void valueWithOneSlash() {
assertEquals(
"{\"notes_text\": \"\\\\\"}",
new FixedJson("{\"notes_text\": \"\\\"}").value()
);
}
@Test
public void valueWithOneSlashAndAnotherValue() {
assertEquals(
"{\"notes_text\": \"\\\\\", \"hello\": \"world\"}",
new FixedJson("{\"notes_text\": \"\\\", \"hello\": \"world\"}").value()
);
}
@Test
public void valueWithTwoSlashes() {
assertEquals(
"{\"notes_text\": \"\\\\\\\\\"}",
new FixedJson("{\"notes_text\": \"\\\\\"}").value()
);
}
@Test
public void valueWithTwoSlashesAndAnotherValue() {
assertEquals(
"{\"notes_text\": \"\\\\\\\\\", \"hello\": \"world\"}",
new FixedJson("{\"notes_text\": \"\\\\\", \"hello\": \"world\"}").value()
);
}
}
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
public class EscapedTest {
@Test
public void doubleQuotesTest() {
assertEquals(
new Escaped("\"").value(),
"\\\""
);
assertEquals(
new Escaped("abc\"def").value(),
"abc\\\"def"
);
}
@Test
public void slashesTest() {
assertEquals(
new Escaped("\\").value(),
"\\\\"
);
assertEquals(
new Escaped("abc\\def").value(),
"abc\\\\def"
);
}
@Test
public void mixedTest() {
assertEquals(
new Escaped("\\\"").value(),
"\\\\\\\""
);
assertEquals(
new Escaped("abc\\\"def").value(),
"abc\\\\\\\"def"
);
}
}
Полный рабочий пример
import java.util.regex.Pattern;
public class FixedJsonExample {
public static void main(String[] args) {
String invalidJson = "{\"notes_text\":\"\"\"}";
final String fixedJson = new FixedJson(invalidJson).value();
System.out.println("fixedJson = " + fixedJson);
}
public static class FixedJson {
private final String target;
private final Pattern pattern;
public FixedJson(String target) {
this(
target,
Pattern.compile("\"(.+?)\"[^\\w\"]")
);
}
public FixedJson(String target, Pattern pattern) {
this.target = target;
this.pattern = pattern;
}
public String value() {
return this.pattern.matcher(this.target).replaceAll(
matchResult -> {
StringBuilder sb = new StringBuilder();
sb.append(
matchResult.group(),
0,
matchResult.start(1) - matchResult.start(0)
);
sb.append(
new Escaped(
new Escaped(matchResult.group(1)).value()
).value()
);
sb.append(
matchResult.group().substring(
matchResult.group().length() - (matchResult.end(0) - matchResult.end(1))
)
);
return sb.toString();
}
);
}
}
public static class Escaped {
private final String target;
private final Pattern pattern;
public Escaped(String target) {
this(
target,
Pattern.compile("[\\\\\"]")
);
}
public Escaped(String target, Pattern pattern) {
this.target = target;
this.pattern = pattern;
}
public String value() {
return this.pattern
.matcher(this.target)
.replaceAll("\\\\$0");
}
}
}
И его STDOUT:
fixedJson = {"notes_text":"\""}
This JSON может быть подтверждено с помощью следующего инструмента: https://jsonlint.com/
объяснение регулярного выражения
FixedJson
класс
Первый класс FixedJson
использует регулярное выражение для сопоставления JSON леммы: все, что находится в двойных кавычках (включая неуместные двойные кавычки).
Подробнее см. Интерактивный пример здесь: https://regexr.com/54blf
Escaped
class
Второй класс Escaped
использует регулярное выражение для сопоставления косой черты или двойных кавычек. Это необходимо для их экранирования.
Подробнее см. Интерактивный пример здесь: https://regexr.com/54bm1