Repository: incubator-apex-malhar Updated Branches: refs/heads/devel-3 d116d9406 -> 2f97e7a40
- MLHR-1908 Added helper method to deserialize JSONObject into a Map. Project: http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/commit/65742586 Tree: http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/tree/65742586 Diff: http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/diff/65742586 Branch: refs/heads/devel-3 Commit: 65742586b116c7faa14f75e58745c1cc437ebb4e Parents: 7803359 Author: Timothy Farkas <[email protected]> Authored: Tue Nov 17 10:29:12 2015 -0800 Committer: Timothy Farkas <[email protected]> Committed: Tue Nov 17 16:39:08 2015 -0800 ---------------------------------------------------------------------- .../datatorrent/lib/appdata/gpo/GPOUtils.java | 340 +++++++++++++------ .../lib/appdata/gpo/GPOUtilsTest.java | 32 +- .../test/resources/deserializeToMapTest.json | 5 + 3 files changed, 266 insertions(+), 111 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/blob/65742586/library/src/main/java/com/datatorrent/lib/appdata/gpo/GPOUtils.java ---------------------------------------------------------------------- diff --git a/library/src/main/java/com/datatorrent/lib/appdata/gpo/GPOUtils.java b/library/src/main/java/com/datatorrent/lib/appdata/gpo/GPOUtils.java index 239dab7..605c6b9 100644 --- a/library/src/main/java/com/datatorrent/lib/appdata/gpo/GPOUtils.java +++ b/library/src/main/java/com/datatorrent/lib/appdata/gpo/GPOUtils.java @@ -20,22 +20,22 @@ package com.datatorrent.lib.appdata.gpo; import java.io.Serializable; import java.lang.reflect.Array; - import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; - import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.apache.commons.lang3.mutable.MutableInt; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + import com.datatorrent.lib.appdata.schemas.Fields; import com.datatorrent.lib.appdata.schemas.FieldsDescriptor; import com.datatorrent.lib.appdata.schemas.ResultFormatter; @@ -123,6 +123,55 @@ public class GPOUtils } /** + * This method deserializes the fields in the given {@link FieldsDescriptor} into a map. + * @param fieldsDescriptor The {@link FieldsDescriptor} to fetch fields from. + * @param dpou The {@link JSONObject} which contains the fields whose values need to be fetched. + * @return A {@link Map} whose keys are field names, and whose values are possible values for those fields. + */ + public static Map<String, Set<Object>> deserializeToMap(FieldsDescriptor fieldsDescriptor, + JSONObject dpou) + { + Map<String, Set<Object>> keyToValues = Maps.newHashMap(); + + for (String key : fieldsDescriptor.getFields().getFields()) { + if (!dpou.has(key)) { + throw new IllegalArgumentException("The given key " + key + " is not contained in the given JSON"); + } + + Set<Object> keyValues; + Object keyValue; + + try { + keyValue = dpou.get(key); + } catch (JSONException ex) { + throw new IllegalStateException("This should never happen", ex); + } + + if (keyValue instanceof JSONArray) { + + JSONArray ja = (JSONArray) keyValue; + keyValues = Sets.newHashSetWithExpectedSize(ja.length()); + + Type type = fieldsDescriptor.getType(key); + + for (int index = 0; index < ja.length(); index++) { + keyValues.add(getFieldFromJSON(type, ja, index)); + } + + } else if (keyValue instanceof JSONObject) { + throw new UnsupportedOperationException("Cannot extract objects from JSONObjects"); + } else { + keyValues = Sets.newHashSetWithExpectedSize(1); + keyValues.add(getFieldFromJSON(fieldsDescriptor, key, dpou)); + } + + keyToValues.put(key, keyValues); + } + + return keyToValues; + } + + /** * This is a helper method for deserialization of a GPOMutable from JSON. It allows you to select a field from * a JSONObject in a json array and set it on the provided GPOMutable object. The format of the JSONArray should * be the following: @@ -178,178 +227,224 @@ public class GPOUtils */ public static void setFieldFromJSON(GPOMutable gpo, String field, JSONObject jo) { - Type type = gpo.getFieldDescriptor().getType(field); - - if(type == Type.BOOLEAN) { - Boolean val; - - try { - val = jo.getBoolean(field); - } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " + field + " does not have a valid bool value.", ex); - } + Object val = getFieldFromJSON(gpo.getFieldDescriptor(), field, jo); + gpo.setFieldGeneric(field, val); + } - gpo.setFieldGeneric(field, val); - } - else if(type == Type.BYTE) { - int val; + /** + * This method gets the given field from the given {@link JSONObject} and converts the field to an object + * of the type specified in the given {@link FieldsDescriptor}. + * @param fd The {@link FieldsDescriptor} describing the type of each field. + * @param field The field to retrieve from the given {@link JSONObject}. + * @param jo The {@link JSONObject} to retrieve a field from. + * @return The value of the given field converted to an object of the correct type. + */ + public static Object getFieldFromJSON(FieldsDescriptor fd, String field, JSONObject jo) + { + Type type = fd.getType(field); + int intVal = 0; + if(numericTypeIntOrSmaller(type)) { try { - val = jo.getInt(field); - } - catch(JSONException ex) { + intVal = jo.getInt(field); + } catch (JSONException ex) { throw new IllegalArgumentException("The key " + field - + " does not have a valid byte value.", ex); + + " does not have a valid " + + type + + " value.", ex); } - if(val < (int)Byte.MIN_VALUE) { + if (type != Type.INTEGER && !insideRange(type, intVal)) { throw new IllegalArgumentException("The key " + field + " has a value " - + val - + " which is too small to fit into a byte."); + + intVal + + " which is out of range for a " + + type + + "."); } + } - if(val > (int)Byte.MAX_VALUE) { + if (type == Type.BOOLEAN) { + try { + return jo.getBoolean(field); + } catch (JSONException ex) { + throw new IllegalArgumentException("The key " + field + " does not have a valid bool value.", ex); + } + } else if (type == Type.BYTE) { + return ((byte) intVal); + } else if (type == Type.SHORT) { + return ((short) intVal); + } else if (type == Type.INTEGER) { + return intVal; + } else if (type == Type.LONG) { + try { + return jo.getLong(field); + } catch (JSONException ex) { throw new IllegalArgumentException("The key " + field - + " has a value " - + val - + " which is too larg to fit into a byte."); + + " does not have a valid long value.", + ex); } - - gpo.setField(field, (byte)val); - } - else if(type == Type.SHORT) { - int val; + } else if (type == Type.CHAR) { + String val; try { - val = jo.getInt(field); - } - catch(JSONException ex) { + val = jo.getString(field); + } catch (JSONException ex) { throw new IllegalArgumentException("The key " + field - + " does not have a valid short value.", + + " does not have a valid character value.", ex); } - if(val < (int)Short.MIN_VALUE) { + if (val.length() != 1) { throw new IllegalArgumentException("The key " + field + " has a value " + val - + " which is too small to fit into a short."); + + " that is not one character long."); } - if(val > (int)Short.MAX_VALUE) { + return val.charAt(0); + } else if (type == Type.STRING) { + try { + return jo.getString(field); + } catch (JSONException ex) { throw new IllegalArgumentException("The key " + field - + " has a value " - + val - + " which is too large to fit into a short."); + + " does not have a valid string value.", + ex); } - - gpo.setField(field, (short)val); - } - else if(type == Type.INTEGER) { - int val; - + } else if (type == Type.DOUBLE) { try { - val = jo.getInt(field); + return jo.getDouble(field); + } catch (JSONException ex) { + throw new IllegalArgumentException("The key " + + field + + " does not have a valid double value.", + ex); } - catch(JSONException ex) { + } else if (type == Type.FLOAT) { + try { + return (float)jo.getDouble(field); + } catch (JSONException ex) { throw new IllegalArgumentException("The key " + field - + " does not have a valid int value.", + + " does not have a valid double value.", ex); } - - gpo.setField(field, val); + } else { + throw new UnsupportedOperationException("The type " + type + " is not supported."); } - else if(type == Type.LONG) { - long val; + } + + /** + * This method gets an object of the given {@link Type} from the given {@link JSONArray} at the + * given index. + * @param type The {@link Type} of the object to retrieve from the {@link JSONArray}. + * @param ja The {@link JSONArray} to retrieve objects from. + * @param index The index of the object in the {@link JSONArray} to retrieve. + * @return The object retrieved from the {@link JSONArray}. + */ + public static Object getFieldFromJSON(Type type, JSONArray ja, int index) + { + int intVal = 0; + if(numericTypeIntOrSmaller(type)) { try { - val = jo.getLong(field); + intVal = ja.getInt(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + + " does not have a valid " + + type + + " value.", ex); + } + + if (type != Type.INTEGER && !insideRange(type, intVal)) { + throw new IllegalArgumentException("The index " + + index + + " has a value " + + intVal + + " which is out of range for a " + + type + + "."); } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " - + field + } + + if (type == Type.BOOLEAN) { + try { + return ja.getBoolean(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + index + " does not have a valid bool value.", ex); + } + } else if (type == Type.BYTE) { + return ((byte) intVal); + } else if (type == Type.SHORT) { + return ((short) intVal); + } else if (type == Type.INTEGER) { + return intVal; + } else if (type == Type.LONG) { + try { + return ja.getLong(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + " does not have a valid long value.", ex); } - - gpo.setField(field, val); - } - else if(type == Type.CHAR) { + } else if (type == Type.CHAR) { String val; try { - val = jo.getString(field); - } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " - + field + val = ja.getString(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + " does not have a valid character value.", ex); } - if(val.length() != 1) { - throw new IllegalArgumentException("The key " - + field + if (val.length() != 1) { + throw new IllegalArgumentException("The index " + + index + " has a value " + val + " that is not one character long."); } - gpo.setField(field, val.charAt(0)); - } - else if(type == Type.STRING) { - String val; - + return val.charAt(0); + } else if (type == Type.STRING) { try { - val = jo.getString(field); - } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " - + field + return ja.getString(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + " does not have a valid string value.", ex); } - - gpo.setField(field, val); - } - else if(type == Type.DOUBLE) { - Double val; - + } else if (type == Type.DOUBLE) { try { - val = jo.getDouble(field); - } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " - + field + return ja.getDouble(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + " does not have a valid double value.", ex); } - - gpo.setFieldGeneric(field, val); - } - else if(type == Type.FLOAT) { - Float val; - + } else if (type == Type.FLOAT) { try { - val = (float)jo.getDouble(field); - } - catch(JSONException ex) { - throw new IllegalArgumentException("The key " - + field + return (float) ja.getDouble(index); + } catch (JSONException ex) { + throw new IllegalArgumentException("The index " + + index + " does not have a valid double value.", ex); } - - gpo.setFieldGeneric(field, val); + } else { + throw new UnsupportedOperationException("The type " + type + " is not supported."); } } @@ -2690,4 +2785,33 @@ public class GPOUtils return values; } + + /** + * Determines if the given value is within the range of the specified type. + * @param type The type to determine the range of. Valid types can be byte or short. + * @param val The value to check the range of. + * @return True if the given int value is within the range of the specified type, false otherwise. + */ + public static boolean insideRange(Type type, int val) { + switch(type) { + case BYTE: { + return !(val < (int)Byte.MIN_VALUE || val > (int)Byte.MAX_VALUE); + } + case SHORT: { + return !(val < (int)Short.MIN_VALUE || val > (int)Short.MAX_VALUE); + } + default: + throw new UnsupportedOperationException("This operation is not supported for the type " + type); + } + } + + /** + * Returns true if the given type is of type byte, short, or integer. + * @param type The type to check. + * @return True if the given type is of type byte, short or integer. + */ + public static boolean numericTypeIntOrSmaller(Type type) + { + return type == Type.BYTE || type == Type.SHORT || type == Type.INTEGER; + } } http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/blob/65742586/library/src/test/java/com/datatorrent/lib/appdata/gpo/GPOUtilsTest.java ---------------------------------------------------------------------- diff --git a/library/src/test/java/com/datatorrent/lib/appdata/gpo/GPOUtilsTest.java b/library/src/test/java/com/datatorrent/lib/appdata/gpo/GPOUtilsTest.java index e3bb1be..0e1aee3 100644 --- a/library/src/test/java/com/datatorrent/lib/appdata/gpo/GPOUtilsTest.java +++ b/library/src/test/java/com/datatorrent/lib/appdata/gpo/GPOUtilsTest.java @@ -20,9 +20,7 @@ package com.datatorrent.lib.appdata.gpo; import java.util.List; import java.util.Map; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.Set; import org.codehaus.jettison.json.JSONObject; import org.junit.Assert; @@ -32,7 +30,12 @@ import org.slf4j.LoggerFactory; import org.apache.commons.lang3.mutable.MutableInt; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + import com.datatorrent.lib.appdata.schemas.FieldsDescriptor; +import com.datatorrent.lib.appdata.schemas.SchemaUtils; import com.datatorrent.lib.appdata.schemas.Type; public class GPOUtilsTest @@ -253,6 +256,29 @@ public class GPOUtilsTest } @Test + @SuppressWarnings("AssertEqualsBetweenInconvertibleTypes") + public void testDeserializeToMap() throws Exception + { + Map<String, Type> fieldToType = Maps.newHashMap(); + fieldToType.put("name", Type.STRING); + fieldToType.put("longvals", Type.LONG); + fieldToType.put("stringvals", Type.STRING); + + FieldsDescriptor fd = new FieldsDescriptor(fieldToType); + String json = SchemaUtils.jarResourceFileToString("deserializeToMapTest.json"); + + Map<String, Set<Object>> resultMap = GPOUtils.deserializeToMap(fd, new JSONObject(json)); + + Set<Object> names = resultMap.get("name"); + Set<Object> longvals = resultMap.get("longvals"); + Set<Object> stringvals = resultMap.get("stringvals"); + + Assert.assertEquals(Sets.newHashSet("tim"), names); + Assert.assertEquals(Sets.newHashSet(1L, 2L, 3L), longvals); + Assert.assertEquals(Sets.newHashSet("a", "b", "c"), stringvals); + } + + @Test public void objectSerdeTest() { Map<String, Type> fieldToTypeKey = Maps.newHashMap(); http://git-wip-us.apache.org/repos/asf/incubator-apex-malhar/blob/65742586/library/src/test/resources/deserializeToMapTest.json ---------------------------------------------------------------------- diff --git a/library/src/test/resources/deserializeToMapTest.json b/library/src/test/resources/deserializeToMapTest.json new file mode 100644 index 0000000..b86c747 --- /dev/null +++ b/library/src/test/resources/deserializeToMapTest.json @@ -0,0 +1,5 @@ +{ + "name": "tim", + "longvals": [1, 2, 3], + "stringvals": ["a", "b", "c"] +}
