首页 > 解决方案 > 将带有特殊字符的字符串转换为 JSONObject

问题描述

我的输入字符串为"{"notes_text": "someString"}". someString 可以包含 \ 或 "。我需要创建一个 JSONObject 来表示这个字符串。

如果它包含特殊字符,只需使用将字符串作为输入的构造函数将引发异常。

我已经尝试了几种方法来做到这一点

我尝试使用正则表达式,但无法理解替换字符串str.replaceAll("\\\".*\\\".*\\\"", replacemntString)

标签: javajson

解决方案


课程

这里有两个类可以解决损坏 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");
    }
}

单元测试

我已经编写了单元测试来证明正确性:

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");
        }
    }
}

及其标准输出:

fixedJson = {"notes_text":"\""}

可以使用以下工具验证此 JSON:https ://jsonlint.com/

正则表达式解释

FixedJson班级

第一类FixedJson使用正则表达式来匹配 JSON 引理:双引号之间的所有内容(包括放错位置的双引号)。

有关更多详细信息,请参见此处的交互式示例:https ://regexr.com/54blf

Escaped班级

第二类Escaped使用正则表达式来匹配斜杠或双引号。这是他们逃跑的必要条件。

有关更多详细信息,请参见此处的交互式示例:https ://regexr.com/54bm1


推荐阅读