Fix JSON for ScalarTypePostgresHstore with field name and nulls

This commit is contained in:
Rob Bygrave
2014-11-13 00:49:20 +13:00
parent 04e77d9949
commit 8a4d78a3f5
4 changed files with 72 additions and 31 deletions
@@ -8,6 +8,7 @@ import java.util.Map;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
/**
* Utility that converts between JSON content and simple java Maps/Lists.
@@ -55,7 +56,17 @@ public class EJson {
public static Map<String,Object> parseObject(JsonParser parser) throws IOException {
return EJsonReader.parseObject(parser);
}
/**
* Parse the json and return as a Map taking a JsonParser and a starting token.
* <p>
* Used when the first token is checked to see if the value is null prior to calling this.
* </p>
*/
public static Map<String,Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return EJsonReader.parseObject(parser, token);
}
/**
* Parse the json and return as a List.
* @throws IOException
@@ -32,6 +32,11 @@ class EJsonReader {
return (Map<String, Object>) parse(parser);
}
@SuppressWarnings("unchecked")
static Map<String, Object> parseObject(JsonParser parser, JsonToken token) throws IOException {
return (Map<String, Object>)parse(parser, token);
}
@SuppressWarnings("unchecked")
static List<Object> parseList(String json) throws IOException {
return (List<Object>) parse(json);
@@ -56,7 +61,11 @@ class EJsonReader {
}
static Object parse(JsonParser parser) throws IOException {
return new EJsonReader(parser).parseJson();
return parse(parser, null);
}
static Object parse(JsonParser parser, JsonToken token) throws IOException {
return new EJsonReader(parser).parseJson(token);
}
private final JsonParser parser;
@@ -106,28 +115,27 @@ class EJsonReader {
currentContext.setValueNull();
}
private Object parseJson() {
private Object parseJson(JsonToken token) throws IOException {
try {
JsonToken token = parser.nextToken();
if (token == null) {
// no initial token so expect to read START_OBJECT or similar
token = parser.nextToken();
if (JsonToken.VALUE_NULL == token) {
return null;
}
stack = new Stack();
// it is a object or array, process the first JsonToken
processJsonToken(token);
// process the rest of the object or array
while (depth > 0) {
token = parser.nextToken();
processJsonToken(token);
}
return currentContext.getValue();
} catch (IOException e) {
throw new RuntimeException(e);
}
stack = new Stack();
// it is a object or array, process the first JsonToken
processJsonToken(token);
// process the rest of the object or array
while (depth > 0) {
token = parser.nextToken();
processJsonToken(token);
}
return currentContext.getValue();
}
/**
@@ -48,7 +48,7 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
if (value == null) {
return null;
}
if (value instanceof Map == false) {
if (!(value instanceof Map)) {
throw new RuntimeException("Expecting Hstore to return as Map but got type "+value.getClass());
}
return new ModifyAwareMap((Map)value);
@@ -111,12 +111,21 @@ public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
@Override
public void jsonWrite(JsonGenerator ctx, String name, Object value) throws IOException {
EJson.write(value, ctx);
// write the field name followed by the Map/JSON Object
if (value == null) {
ctx.writeNullField(name);
} else {
ctx.writeFieldName(name);
EJson.write(value, ctx);
}
}
@Override
public Object jsonRead(JsonParser ctx, JsonToken event) throws IOException {
return EJson.parseObject(ctx);
// at this point the BeanProperty has read the START_OBJECT token
// to check for a null value. Pass the START_OBJECT token through to
// the EJson parsing so that it knows the first token has been read
return EJson.parseObject(ctx, event);
}
}
@@ -57,41 +57,54 @@ public class ScalarTypePostgresHstoreTest {
@Test
public void testJsonWrite() throws Exception {
assertEquals("{\"key\":null}", generateJson(null));
Map<String,Object> map = new LinkedHashMap<String, Object>();
assertEquals("{}", generateJson(map));
assertEquals("{\"key\":{}}", generateJson(map));
map.put("name", "rob");
assertEquals("{\"name\":\"rob\"}", generateJson(map));
assertEquals("{\"key\":{\"name\":\"rob\"}}", generateJson(map));
map.put("age", 12);
assertEquals("{\"name\":\"rob\",\"age\":12}", generateJson(map));
assertEquals("{\"key\":{\"name\":\"rob\",\"age\":12}}", generateJson(map));
}
@Test
public void testJsonRead() throws Exception {
Map<String, Object> map = parse("{\"name\":\"rob\"}");
Map<String, Object> map = parseHstore("{\"name\":\"rob\"}");
assertEquals(1, map.size());
assertEquals("rob", map.get("name"));
map = parse("{\"name\":\"rob\",\"age\":12}");
map = parseHstore("{\"name\":\"rob\",\"age\":12}");
assertEquals(2, map.size());
assertEquals("rob", map.get("name"));
assertEquals(12L, map.get("age"));
}
private Map<String,Object> parse(String json) throws IOException {
private Map<String,Object> parseHstore(String json) throws IOException {
JsonParser parser = jsonFactory.createParser(json);
return (Map<String,Object>)hstore.jsonRead(parser, JsonToken.FIELD_NAME);
// BeanProperty reads the first token checking for null so
// simulate that here
JsonToken token = parser.nextToken();
assertEquals(JsonToken.START_OBJECT, token);
return (Map<String,Object>)hstore.jsonRead(parser, token);
}
private String generateJson(Map<String, Object> emptyMap) throws IOException {
private String generateJson(Map<String, Object> map) throws IOException {
StringWriter writer = new StringWriter();
JsonGenerator generator = jsonFactory.createGenerator(writer);
hstore.jsonWrite(generator, "name", emptyMap);
// wrap in an object to form proper json
generator.writeStartObject();
hstore.jsonWrite(generator, "key", map);
generator.writeEndObject();
generator.flush();
return writer.toString();
}