Search code examples
javaredisredisgraph

RedisGraph: how to persist properties in data containing BOTH single AND double quotes?


I am testing RedisGraph as a way to store my data which originates from a client as JSON.

The JSON passes through a bean for validation etc and I use Jackson to serialise the bean so the RedisGraph string is in the correct format. For completeness on that formatting step see the sample code at the end.

The data properties might contain sinqle quotes in valid JSON format eg: O'Toole

{ "name" : "Peter O'Toole", "desc" : "An actors actor" }

I can use a formatter as per the code block at the end to get the JSON into a format the RedisGraph command will allow which copes with the single quotes (without me needing to escape the data content - ie it can use what the client sends). eg this works:

GRAPH.QUERY movies "CREATE (:Actor {name:\"Peter O'Toole\", desc:\"An actors actor\", actor_id:1})"

So far, so good.

Now, the problem: I am having trouble with the syntax to persist original JSON where it ALSO contains escaped double quotes. eg:

{ "name" : "Peter O'Toole", "desc" : "An \"actors\" actor" }

I don't want to have to escape or wrap the desc property value because it is already escaped as valid JSON. But then how do I construct the RedisGraph command so it persists the properties using the values it is given? ie containing escaped double quotes.

In other words, this throws a parsing error because of the \" in the desc property.

GRAPH.QUERY movies "CREATE (:Actor {name:\"Peter O'Toole\", desc:\"An \"actors\" actor\", actor_id:1})"

Given it would be quite common to want to persist data containing valid JSON escaped double quotes \" AND unescaped single quotes, there must be a way to do this. eg name and address data.

Any ideas?

Thanks, Murray.

PS: this doesnt work either: it chokes on the embedded ' in O'Toole

GRAPH.QUERY movies "CREATE (:Actor {name:\'Peter O'Toole\', desc:\'an \"actors\" actor\', actor_id:3})"
// \u007F is the "delete" character.
// This is the highest char value Jackson allows and is
// unlikely to be in the JSON (hopefully!)
JsonFactory builder = new JsonFactoryBuilder().quoteChar('\u007F').build();

ObjectMapper objectMapper = new ObjectMapper(builder);

// Set pretty printing of json
objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

// Do not surround property names with quotes. ie { firstName : "Peter" }
objectMapper.configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false);

// Make a Person
Person person = new Person("Peter", "O'Toole");

// Set the desc property using embedded quotes
person.setDesc("An \"actors\" actor");

// Convert Person to JSON
String json = objectMapper.writeValueAsString(person);

// Now convert your json to escape the double quotes around the string properties:
String j2 = json.replaceAll("\u007F", "\\\\\"");

System.out.println(j2);

This yields:

{
  firstName : \"Peter\",
  lastName : \"O'Toole\",
  desc : \"An \"actors\" actor\"
}

which is in a format Redis GRAPH.QUERY movies "CREATE..." can use (apart from the issue with \"actors\" as discussed above).


Solution

  • OK. The issue was an artefact of trying to test the syntax by entering the commands into RedisInsight directly. As it turns out all one needs to do is to remove the double quotes from the valid json.

    So, to be clear, based on normal valid json coming from the client app, the formatter test is:

    ObjectMapper objectMapper = new ObjectMapper();
    
    // (Optional) Set pretty printing of json
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
    
    // Do not surround property names with quotes. ie { firstname : "Peter" }
    objectMapper.configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false);
    
    // Make a Person
    // For this example this is done directly, 
    // although in the Java this is done using
    // objectMapper.readValue(incomingJson, Person.class)
    
    Person person = new Person("Peter", "O'Toole");
    
    // Set the desc property using escaped double quotes
    person.setDesc("An \"actor's\" actor");
    
    // Convert Person to JSON without quoted property names
    String json = objectMapper.writeValueAsString(person);
    
    System.out.println(json);
    

    yields:

    {
      firstname : "Peter",
      lastname : "O'Toole",
      desc : "An \"actor's\" actor"
    }
    

    and the command string is consumed by the Vertx Redis:

    Vertx vertx = Vertx.vertx();
    private final Redis redisClient;
    
    // ...
    
    redisClient = Redis.createClient(vertx);
    
    String cmdStr = "CREATE (:Actor {firstname:"Peter", lastname: "O'Toole", desc:"An \"actor's\" actor", actor_id:1})";
    
    Future<String> futureResponse = redisClient.send(Request.cmd(Command.GRAPH_QUERY).arg("movies").arg(cmdStr))
        .compose(response -> {
          Log.info("createRequest response=" + response.toString());
    
          return Future.succeededFuture("OK");
        })
        .onFailure(failure -> {
          Log.error("createRequest failure=" + failure.toString());
        });
    
    

    :-)