1

My String input is something like:

{"key1":"value1","key2":"{\"key2_1\":\"123\",\"key2_2\":\"456\",\"key2_3\":\"33333\"}"}

The value fields in the above JSON could contain characters such as ", \ and so on. For your convience here is the formatted version:

{
    "key1": "value1",
    "key2": "{\"key2_1\":\"123\",\"key2_2\":\"456\",\"key2_3\":\"33333\"}"
}

I want to use Gson to convert the String into a Foo Object:

class Foo {
    private String key1;
    private Bar key2;
    ...
}

class Bar {
    private String key2_1;
    private String key2_2;
    private String key2_3;
    ...
}

Here's my regular expression:

String regexp = "\\{[\"a-zA-Z-0-9:,\\\\]*\"key2\":\"\\{\\\\\"key2_1\\\\\":\\\\\"[a-zA-Z0-9]*\\\\\",\\\\\"key2_2\\\\\":\\\\\"[a-zA-Z0-9]*\\\\\",\\\\\"key2_3\\\\\":\\\\\"[a-zA-Z0-9]*\\\\\"\\}\"\\}[\"a-zA-Z-0-9:,\\\\]*";
Pattern pattern = Pattern.compile(regexp);
Matcher matcher = pattern.matcher(text);
if(matcher.matches()) {
    ... // TODO: Replace all "{, \" and }" but How???
}

How could I use this regular expression to replace all "{, \".and "} into {, ", } without changing the keys and values in JSON?

Finding the sub-string and using String's replace method will be my backup solution.

Again, my ultimate goal is to parse the input String into my Foo object. Is there a better way rather than using regular expression?

Thank you!

Top.Deck
  • 1,077
  • 3
  • 16
  • 31
  • Shouldn't it just work fine like this? – Shark Jun 23 '17 at 15:57
  • @Shark Matcher only returns a boolean. I couldn't convert the String into object by "just like this". Please notice key2's input value is a String and in the Foo object the corresponding field is Bar object. – Top.Deck Jun 23 '17 at 16:01
  • What I mean is, the JSON already contains a `Bar` object; shouldn't it parse fine into a `Foo` instance? – Shark Jun 23 '17 at 16:39
  • @Shark The JSON contains a String for Bar object instead of a normal JSON object. Parse the String directly into Foo object will raise an error. – Top.Deck Jun 23 '17 at 17:56

2 Answers2

3

The golden rule is DO NOT USE REGULAR EXPRESSIONS FOR SUCH THINGS :) My advice is too look to the JSON mappers, for example Jackson

All you need to do:

  1. Fix JSON in your example, here is no need double quotes around nested {
  2. Add Jackson dependencies

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>${jackson.version}</version>
    </dependency>
    
  3. Create ObjectMapper and let him handle your JSON string

    ObjectMapper mapper = new ObjectMapper();
    Foo foo = mapper.readValue( "{\"key1\":\"value1\",\"key2\":"
         + " {\"key2_1\":\"123\",\"key2_2\":\"456\",\"key2_3\":\"33333\"}}", Foo.class);
    System.out.println(foo.toString());
    

Hope it helps!

Sergey Prokofiev
  • 1,815
  • 1
  • 12
  • 21
  • The problem is the input JSON is not under my control :( – Top.Deck Jun 23 '17 at 16:19
  • @Top.Deck this is not a problem, the solution will works with any valid JSON input. Nested special characters will be processed correctly. – Sergey Prokofiev Jun 23 '17 at 16:20
  • the input at your code actually would be "{\"key1\":\"value1\",\"key2\":" + " {\\\"key2_1\\\":\\\"123\\\",\\\"key2_2\\\":\\\"456\",\\\"key2_3\\\":\\\"33333\\\"}}", so the code thinks key2 has a String value instead of a nested JSON. – Top.Deck Jun 23 '17 at 16:23
  • @Top.Deck the output of my code will be `Foo [key1=value1, key2=Bar [key2_1=123, key2_2=456, key2_3=33333]]` . I suppose that is your expectations, according showed dao classes. – Sergey Prokofiev Jun 23 '17 at 16:25
  • I totally agree with you that your output is the object I want. But your input is a little bit wrong here. Your input is a nested JSON while my key2 contains a String. Could you replace the Foo foo line in your code into Foo foo = mapper.readValue("{\"key1\":\"value1\",\"key2\":" + " {\\\"key2_1\\\":\\\"123\\\",\\\"key2_2\\\":\\\"456\",\\\"key‌​2_3\\\":\\\"33333\\\‌​"}}"); and see what's the output? – Top.Deck Jun 23 '17 at 16:28
  • @Top.Deck, sure - here you go - "Invalid escape sequence (valid ones are \b \t \n \f \r \" \' \\ )". And, ... it's expected actually. – Sergey Prokofiev Jun 23 '17 at 16:37
  • Since I do not control the input... this is the problem I'm facing now if I convert the String directly into the Foo object. – Top.Deck Jun 23 '17 at 17:55
1

After digging further, I find those two links:

Gson custom deseralizer for one variable in an object

Gson custom seralizer for one variable (of many) in an object using TypeAdapter

I achieved what I want by registering my own JsonDeserializer to GsonBuilder:

private static GsonBuilder gsonBuilder = new GsonBuilder().registerTypeAdapter(Bar.class, new JsonDeserializer<Bar>() {

    @Override
    public Bar deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException {
        Bar result = new Bar();
        String regexp = "\"\\{\\\\\"key2_1\\\\\":\\\\\"(?s).*\\\\\".\\\\\"key2_2\\\\\":\\\\\"(?s).*\\\\\",\\\\\"key2_3\\\\\":\\\\\"(?s).*\\\\\"\\}\"";
        Pattern pattern = Pattern.compile(regexp);
        Matcher matcher = pattern.matcher(json.toString());
        String modifiedJsonStr = json.toString();
        if(matcher.matches()) {
            modifiedJsonStr = json.toString().replace("\"{", "{").replace("}\"", "}").replace("\\\"", "\"");
        }
        result = new Gson().fromJson(modifiedJsonStr, Bar.class);
        return result;
    }
});

Cheers.

Top.Deck
  • 1,077
  • 3
  • 16
  • 31