Skip to content

Commit

Permalink
GEODE-6327: Ability to specify identity fields on JSON documents
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmellawatt committed May 2, 2019
1 parent d1ddd44 commit 8557957
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
*/
package org.apache.geode.pdx;

import static org.assertj.core.api.Assertions.assertThat;

import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheFactory;
Expand All @@ -28,6 +33,7 @@
* examples.
*/
@Category({RestAPITest.class})
@RunWith(JUnitParamsRunner.class)
public class JSONFormatterBasicJUnitTest {
// This is needed because the JsonFormatter needs to access the PDX region, which requires a
// running Cache.
Expand Down Expand Up @@ -99,7 +105,62 @@ public void emptyObjectParses() {
}

@Test
public void simpleObjectParses() {
JSONFormatter.fromJSON("{\"a\": 2}");
@Parameters({"true", "false"})
public void simpleObjectAsStringParsesWithIdentityField(String usePdxInstanceSortedHelper) {
String originalSortJsonFieldNamesProperty =
System.getProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY);
try {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, usePdxInstanceSortedHelper);
String identityField = "a";
String nonIdentityField = "b";
String nonExistentField = "c";
String jsonObjectString = "{\"a\":2,\"b\":3}";

PdxInstance pdxInstance = JSONFormatter.fromJSON(jsonObjectString, identityField);
String deserializedJsonObjectString = JSONFormatter.toJSON(pdxInstance);

assertThat(pdxInstance.isIdentityField(identityField)).isTrue();
assertThat(pdxInstance.isIdentityField(nonIdentityField)).isFalse();
assertThat(pdxInstance.isIdentityField(nonExistentField)).isFalse();
assertThat(pdxInstance.hasField(identityField)).isTrue();
assertThat(pdxInstance.hasField(nonIdentityField)).isTrue();
assertThat(pdxInstance.hasField(nonExistentField)).isFalse();
assertThat(deserializedJsonObjectString).isEqualTo(jsonObjectString);
} finally {
if (originalSortJsonFieldNamesProperty != null) {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY,
originalSortJsonFieldNamesProperty);
}
}
}

@Test
@Parameters({"true", "false"})
public void simpleObjectAsBytesParsesWithIdentityField(String usePdxInstanceSortedHelper) {
String originalSortJsonFieldNamesProperty =
System.getProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY);
try {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY, usePdxInstanceSortedHelper);
String identityField = "a";
String nonIdentityField = "b";
String nonExistentField = "c";
String jsonObjectString = "{\"a\":2,\"b\":3}";

PdxInstance pdxInstance = JSONFormatter.fromJSON(jsonObjectString.getBytes(), identityField);
byte[] deserializedJsonObjectString = JSONFormatter.toJSONByteArray(pdxInstance);

assertThat(pdxInstance.isIdentityField(identityField)).isTrue();
assertThat(pdxInstance.isIdentityField(nonIdentityField)).isFalse();
assertThat(pdxInstance.isIdentityField(nonExistentField)).isFalse();
assertThat(pdxInstance.hasField(identityField)).isTrue();
assertThat(pdxInstance.hasField(nonIdentityField)).isTrue();
assertThat(pdxInstance.hasField(nonExistentField)).isFalse();
assertThat(deserializedJsonObjectString).isEqualTo(jsonObjectString.getBytes());
} finally {
if (originalSortJsonFieldNamesProperty != null) {
System.setProperty(JSONFormatter.SORT_JSON_FIELD_NAMES_PROPERTY,
originalSortJsonFieldNamesProperty);
}
}
}
}
76 changes: 45 additions & 31 deletions geode-core/src/main/java/org/apache/geode/pdx/JSONFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@

/**
* <p>
* JSONFormatter has a static method {@link JSONFormatter#fromJSON(String)} to convert a JSON
* document into a {@link PdxInstance} and a static method {@link JSONFormatter#toJSON(PdxInstance)}
* to convert a {@link PdxInstance} into a JSON Document.
* JSONFormatter has a static method {@link JSONFormatter#fromJSON(String, String...)} to convert a
* JSON document into a {@link PdxInstance} and a static method
* {@link JSONFormatter#toJSON(PdxInstance)} to convert a {@link PdxInstance} into a JSON Document.
* </p>
* <p>
* Using these methods an applications may convert a JSON document into a PdxInstance for storing in
Expand Down Expand Up @@ -124,33 +124,41 @@ public RegionService getRegionService() {
}

/**
* Converts a JSON document into a PdxInstance
* Converts a String JSON document into a PdxInstance
*
* @return the PdxInstance.
* @throws JSONFormatterException if unable to parse the JSON document
* @param jsonString The JSON String to convert to PDX
* @param identityFields Any desired identity fields on the JSON object to be used for equals and
* hashCode computations
* @throws JSONFormatterException if unable to create the PdxInstance
* @return The PdxInstance
*/
public static PdxInstance fromJSON(String jsonString) {
return new JSONFormatter().toPdxInstance(jsonString);
public static PdxInstance fromJSON(String jsonString, String... identityFields) {
return new JSONFormatter().toPdxInstance(jsonString, identityFields);
}

/**
* Converts a JSON document into a PdxInstance
* Converts a Byte Array JSON document into a PdxInstance
*
* @return the PdxInstance.
* @throws JSONFormatterException if unable to parse the JSON document
* @param jsonByteArray The JSON Object as a byte array to convert to PDX
* @param identityFields Any desired identity fields on the JSON object to be used for equals and
* hashCode computations
* @throws JSONFormatterException if unable to create the PdxInstance
* @return The PdxInstance
*/
public static PdxInstance fromJSON(byte[] jsonByteArray) {
return new JSONFormatter().toPdxInstance(jsonByteArray);
public static PdxInstance fromJSON(byte[] jsonByteArray, String... identityFields) {
return new JSONFormatter().toPdxInstance(jsonByteArray, identityFields);
}

/**
* Converts a JSON document into a PdxInstance
* Converts a JSON document (String or Byte Array) into a PdxInstance
*
* @param json either a json string or a byte[]
* @return the PdxInstance.
* @throws JSONFormatterException if unable to parse the JSON document
* @param json The JSON document (String or Byte Array) to convert to PDX
* @param identityFields Any desired identity fields on the JSON object to be used for equals and
* hashCode computations
* @return The PdxInstance
* @throws JSONFormatterException if unable to create the PdxInstance
*/
public PdxInstance toPdxInstance(Object json) {
public PdxInstance toPdxInstance(Object json, String... identityFields) {
if (regionService != null && regionService instanceof ProxyCache) {
ProxyCache proxyCache = (ProxyCache) regionService;
UserAttributes.userAttributes.set(proxyCache.getUserAttributes());
Expand All @@ -166,7 +174,7 @@ public PdxInstance toPdxInstance(Object json) {
throw new JSONFormatterException("Could not parse the " + json.getClass() + " type");
}
enableJSONParserFeature(jp);
return getPdxInstance(jp, states.NONE, null).getPdxInstance();
return getPdxInstance(jp, states.NONE, null, identityFields).getPdxInstance();
} catch (JsonParseException jpe) {
throw new JSONFormatterException("Could not parse JSON document ", jpe);
} catch (IOException e) {
Expand All @@ -186,10 +194,11 @@ private void enableJSONParserFeature(JsonParser jp) {
}

/**
* Converts a PdxInstance into a JSON document
* Converts a PdxInstance into a JSON document in String form
*
* @return the JSON string.
* @throws JSONFormatterException if unable to create the JSON document
* @param pdxInstance the JSON string.
* @return the JSON string
* JSONFormatterException if unable to create the JSON document
*/
public static String toJSON(PdxInstance pdxInstance) {
return new JSONFormatter().fromPdxInstance(pdxInstance);
Expand All @@ -198,7 +207,8 @@ public static String toJSON(PdxInstance pdxInstance) {
/**
* Converts a PdxInstance into a JSON document
*
* @return the JSON string.
* @param pdxInstance The PdxInstance to convert
* @return the JSON string
* @throws JSONFormatterException if unable to create the JSON document
*/
public String fromPdxInstance(PdxInstance pdxInstance) {
Expand All @@ -213,7 +223,8 @@ public String fromPdxInstance(PdxInstance pdxInstance) {
/**
* Converts a PdxInstance into a JSON document in byte-array form
*
* @return the JSON byte array.
* @param pdxInstance The PdxInstance to convert
* @return the JSON byte array
* @throws JSONFormatterException if unable to create the JSON document
*/
public static byte[] toJSONByteArray(PdxInstance pdxInstance) {
Expand All @@ -223,7 +234,8 @@ public static byte[] toJSONByteArray(PdxInstance pdxInstance) {
/**
* Converts a PdxInstance into a JSON document in byte-array form
*
* @return the JSON byte array.
* @param pdxInstance The PdxInstance to convert
* @return the JSON byte array
* @throws JSONFormatterException if unable to create the JSON document
*/
public byte[] toJsonByteArrayFromPdxInstance(PdxInstance pdxInstance) {
Expand All @@ -235,19 +247,20 @@ public byte[] toJsonByteArrayFromPdxInstance(PdxInstance pdxInstance) {
}
}

private JSONToPdxMapper createJSONToPdxMapper(String className, JSONToPdxMapper parent) {
private JSONToPdxMapper createJSONToPdxMapper(String className, JSONToPdxMapper parent,
String... identityFields) {
if (Boolean.getBoolean(SORT_JSON_FIELD_NAMES_PROPERTY)) {
return new PdxInstanceSortedHelper(className, parent);
return new PdxInstanceSortedHelper(className, parent, identityFields);
} else {
return new PdxInstanceHelper(className, parent);
return new PdxInstanceHelper(className, parent, identityFields);
}
}

private JSONToPdxMapper getPdxInstance(JsonParser jp, states currentState,
JSONToPdxMapper currentPdxInstance) throws IOException {
JSONToPdxMapper currentPdxInstance, String... identityFields) throws IOException {
String currentFieldName = null;
if (currentState == states.OBJECT_START && currentPdxInstance == null) {
currentPdxInstance = createJSONToPdxMapper(null, null);// from getlist
currentPdxInstance = createJSONToPdxMapper(null, null, identityFields);// from getlist
}
while (true) {
JsonToken nt = jp.nextToken();
Expand All @@ -262,7 +275,8 @@ private JSONToPdxMapper getPdxInstance(JsonParser jp, states currentState,
// need to create new PdxInstance
// root object will not name, so create classname lazily from all members.
// child object will have name; but create this as well lazily from all members
JSONToPdxMapper tmp = createJSONToPdxMapper(currentFieldName, currentPdxInstance);
JSONToPdxMapper tmp =
createJSONToPdxMapper(currentFieldName, currentPdxInstance, identityFields);
currentPdxInstance = tmp;
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.Set;

import org.apache.logging.log4j.Logger;

Expand All @@ -36,12 +38,13 @@ public class PdxInstanceHelper implements JSONToPdxMapper {
PdxInstanceFactoryImpl m_pdxInstanceFactory;
PdxInstance m_pdxInstance;
String m_PdxName;// when pdx is member, else null if part of lists
private Set<String> identityFields;

private InternalCache getCache() {
return (InternalCache) CacheFactory.getAnyInstance();
}

public PdxInstanceHelper(String className, JSONToPdxMapper parent) {
public PdxInstanceHelper(String className, JSONToPdxMapper parent, String... identityFields) {
InternalCache cache = getCache();
if (logger.isTraceEnabled()) {
logger.trace("ClassName {}", className);
Expand All @@ -50,6 +53,14 @@ public PdxInstanceHelper(String className, JSONToPdxMapper parent) {
m_parent = parent;
m_pdxInstanceFactory = (PdxInstanceFactoryImpl) cache
.createPdxInstanceFactory(JSONFormatter.JSON_CLASSNAME, false);
initializeIdentityFields(identityFields);
}

public void initializeIdentityFields(String... identityFields) {
this.identityFields = new HashSet<>();
for (String identityField : identityFields) {
this.identityFields.add(identityField);
}
}

@Override
Expand All @@ -71,6 +82,7 @@ public void addStringField(String fieldName, String value) {
logger.trace("addStringField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeObject(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -79,6 +91,7 @@ public void addByteField(String fieldName, byte value) {
logger.trace("addByteField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeByte(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -87,6 +100,7 @@ public void addShortField(String fieldName, short value) {
logger.trace("addShortField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeShort(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -95,6 +109,7 @@ public void addIntField(String fieldName, int value) {
logger.trace("addIntField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeInt(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -103,6 +118,7 @@ public void addLongField(String fieldName, long value) {
logger.trace("addLongField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeLong(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -111,6 +127,7 @@ public void addBigDecimalField(String fieldName, BigDecimal value) {
logger.trace("addBigDecimalField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeObject(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -119,6 +136,7 @@ public void addBigIntegerField(String fieldName, BigInteger value) {
logger.trace("addBigIntegerField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeObject(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -127,6 +145,7 @@ public void addBooleanField(String fieldName, boolean value) {
logger.trace("addBooleanField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeBoolean(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -135,6 +154,7 @@ public void addFloatField(String fieldName, float value) {
logger.trace("addFloatField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeFloat(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -143,6 +163,7 @@ public void addDoubleField(String fieldName, double value) {
logger.trace("addDoubleField fieldName: {}; value: {}", fieldName, value);
}
m_pdxInstanceFactory.writeDouble(fieldName, value);
addIdentityField(fieldName);
}

@Override
Expand All @@ -151,6 +172,7 @@ public void addNullField(String fieldName) {
logger.trace("addNullField fieldName: {}; value: NULL", fieldName);
}
m_pdxInstanceFactory.writeObject(fieldName, null);
addIdentityField(fieldName);
}

@Override
Expand All @@ -159,6 +181,7 @@ public void addListField(String fieldName, PdxListHelper list) {
logger.trace("addListField fieldName: {}", fieldName);
}
m_pdxInstanceFactory.writeObject(fieldName, list.getList());
addIdentityField(fieldName);
}

@Override
Expand Down Expand Up @@ -186,6 +209,12 @@ public void endObjectField(String fieldName) {
m_pdxInstance = m_pdxInstanceFactory.create();
}

private void addIdentityField(String fieldName) {
if (this.identityFields.contains(fieldName)) {
m_pdxInstanceFactory.markIdentityField(fieldName);
}
}

@Override
public PdxInstance getPdxInstance() {
return m_pdxInstance;
Expand Down
Loading

0 comments on commit 8557957

Please sign in to comment.