Преобразовать строку со специальными символами в JSONObject - PullRequest
1 голос
/ 08 мая 2020

У меня входная строка выглядит как "{"notes_text": "someString"}". someString может содержать \ или ". И мне нужно создать JSONObject, представляющий эту строку.

Простое использование конструктора, который принимает строку в качестве входных данных, вызовет исключение, если он содержит специальные символы.

Я пробовал несколько подходов для этого.

  • использовать библиотеку для экранирования символов - это приведет, например, к "{\"notes_text\":\"\"\"}" или "{\"notes_text\":\"\\\"}". И мне нужно будет изменить их в "{\"notes_text\":\"\\\"\"}" и "{\"notes_text\":\"\\\\\"}" соответственно, иначе это снова вызовет исключение при преобразовании в JSON Object. Использование метода replace в строке преобразует все \ "во входной строке
  • don ' t экранировать специальные символы во входной строке и использовать метод замены строки для преобразования \ в \ и "в \". Но это снова заменит все двойные кавычки во входной строке

Я пробовал использовать регулярное выражение, но не могу понять строку замены в str.replaceAll("\\\".*\\\".*\\\"", replacemntString)

1 Ответ

3 голосов
/ 11 мая 2020

Классы

Вот два класса, которые решают начальную проблему со сломанным 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...