package org.json.junit;
import static org.junit.Assert.assertTrue;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Test;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
/**
* Enums are not explicitly supported in JSON-Java. But because enums act like
* classes, all required behavior is already be present in some form.
* These tests explore how enum serialization works with JSON-Java.
*/
public class EnumTest {
/**
* To serialize an enum by its getters, use the JSONObject Object constructor.
* The JSONObject ctor handles enum like any other bean. A JSONobject
* is created whose entries are the getter name/value pairs.
*/
@Test
public void jsonObjectFromEnum() {
// If there are no getters then the object is empty.
MyEnum myEnum = MyEnum.VAL2;
JSONObject jsonObject = new JSONObject(myEnum);
assertTrue("simple enum has no getters", jsonObject.length() == 0);
// enum with a getters should create a non-empty object
MyEnumField myEnumField = MyEnumField.VAL2;
jsonObject = new JSONObject(myEnumField);
// validate JSON content
Object doc = Configuration.defaultConfiguration().jsonProvider()
.parse(jsonObject.toString());
assertTrue("expecting 2 items in top level object", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expecting val 2", "val 2".equals(jsonObject.query("/value")));
assertTrue("expecting 2", Integer.valueOf(2).equals(jsonObject.query("/intVal")));
/**
* class which contains enum instances. Each enum should be stored
* in its own JSONObject
*/
MyEnumClass myEnumClass = new MyEnumClass();
myEnumClass.setMyEnum(MyEnum.VAL1);
myEnumClass.setMyEnumField(MyEnumField.VAL3);
jsonObject = new JSONObject(myEnumClass);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected 2 myEnumField items", "VAL3".equals((JsonPath.read(doc, "$.myEnumField"))));
assertTrue("expected 0 myEnum items", "VAL1".equals((JsonPath.read(doc, "$.myEnum"))));
assertTrue("expecting MyEnumField.VAL3", MyEnumField.VAL3.equals(jsonObject.query("/myEnumField")));
assertTrue("expecting MyEnum.VAL1", MyEnum.VAL1.equals(jsonObject.query("/myEnum")));
}
/**
* To serialize an enum by its set of allowed values, use getNames()
* and the the JSONObject Object with names constructor.
*/
@Test
public void jsonObjectFromEnumWithNames() {
String [] names;
JSONObject jsonObject;
MyEnum myEnum = MyEnum.VAL1;
names = JSONObject.getNames(myEnum);
// The values will be MyEnum fields
jsonObject = new JSONObject(myEnum, names);
// validate JSON object
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 3 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 3);
assertTrue("expected VAL1", MyEnum.VAL1.equals(jsonObject.query("/VAL1")));
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonObject.query("/VAL2")));
assertTrue("expected VAL3", MyEnum.VAL3.equals(jsonObject.query("/VAL3")));
MyEnumField myEnumField = MyEnumField.VAL3;
names = JSONObject.getNames(myEnumField);
// The values will be MyEnmField fields
jsonObject = new JSONObject(myEnumField, names);
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 3 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 3);
assertTrue("expected VAL1", MyEnumField.VAL1.equals(jsonObject.query("/VAL1")));
assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonObject.query("/VAL2")));
assertTrue("expected VAL3", MyEnumField.VAL3.equals(jsonObject.query("/VAL3")));
}
/**
* Verify that enums are handled consistently between JSONArray and JSONObject
*/
@Test
public void verifyEnumConsistency(){
JSONObject jo = new JSONObject();
jo.put("value", MyEnumField.VAL2);
String expected="{\"value\":\"VAL2\"}";
String actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
jo.accumulate("value", MyEnumField.VAL1);
expected="{\"value\":[\"VAL2\",\"VAL1\"]}";
actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
jo.remove("value");
jo.append("value", MyEnumField.VAL1);
expected="{\"value\":[\"VAL1\"]}";
actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
jo.put("value", EnumSet.of(MyEnumField.VAL2));
expected="{\"value\":[\"VAL2\"]}";
actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
JSONArray ja = new JSONArray();
ja.put(MyEnumField.VAL2);
jo.put("value", ja);
actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
jo.put("value", new MyEnumField[]{MyEnumField.VAL2});
actual = jo.toString();
assertTrue("Expected "+expected+" but actual was "+actual, expected.equals(actual));
}
/**
* To serialize by assigned value, use the put() methods. The value
* will be stored as a enum type.
*/
@Test
public void enumPut() {
JSONObject jsonObject = new JSONObject();
MyEnum myEnum = MyEnum.VAL2;
jsonObject.put("myEnum", myEnum);
MyEnumField myEnumField = MyEnumField.VAL1;
jsonObject.putOnce("myEnumField", myEnumField);
// validate JSON content
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level objects", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonObject.query("/myEnum")));
assertTrue("expected VAL1", MyEnumField.VAL1.equals(jsonObject.query("/myEnumField")));
JSONArray jsonArray = new JSONArray();
jsonArray.put(myEnum);
jsonArray.put(1, myEnumField);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString());
assertTrue("expected 2 top level objects", ((List>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonArray.query("/0")));
assertTrue("expected VAL1", MyEnumField.VAL1.equals(jsonArray.query("/1")));
/**
* Leaving these tests because they exercise get, opt, and remove
*/
assertTrue("expecting myEnum value", MyEnum.VAL2.equals(jsonArray.get(0)));
assertTrue("expecting myEnumField value", MyEnumField.VAL1.equals(jsonArray.opt(1)));
assertTrue("expecting myEnumField value", MyEnumField.VAL1.equals(jsonArray.remove(1)));
}
/**
* The default action of valueToString() is to call object.toString().
* For enums, this means the assigned value will be returned as a string.
*/
@Test
public void enumValueToString() {
String expectedStr1 = "\"VAL1\"";
String expectedStr2 = "\"VAL1\"";
MyEnum myEnum = MyEnum.VAL1;
MyEnumField myEnumField = MyEnumField.VAL1;
MyEnumClass myEnumClass = new MyEnumClass();
String str1 = JSONObject.valueToString(myEnum);
assertTrue("actual myEnum: "+str1+" expected: "+expectedStr1,
str1.equals(expectedStr1));
String str2 = JSONObject.valueToString(myEnumField);
assertTrue("actual myEnumField: "+str2+" expected: "+expectedStr2,
str2.equals(expectedStr2));
/**
* However, an enum within another class will not be rendered
* unless that class overrides default toString()
*/
String expectedStr3 = "\"org.json.junit.MyEnumClass@";
myEnumClass.setMyEnum(MyEnum.VAL1);
myEnumClass.setMyEnumField(MyEnumField.VAL1);
String str3 = JSONObject.valueToString(myEnumClass);
assertTrue("actual myEnumClass: "+str3+" expected: "+expectedStr3,
str3.startsWith(expectedStr3));
}
/**
* In whatever form the enum was added to the JSONObject or JSONArray,
* json[Object|Array].toString should serialize it in a reasonable way.
*/
@Test
public void enumToString() {
MyEnum myEnum = MyEnum.VAL2;
JSONObject jsonObject = new JSONObject(myEnum);
String expectedStr = "{}";
assertTrue("myEnum toString() should be empty", expectedStr.equals(jsonObject.toString()));
MyEnumField myEnumField = MyEnumField.VAL2;
jsonObject = new JSONObject(myEnumField);
// validate JSON content
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected val 2", "val 2".equals(jsonObject.query("/value")));
assertTrue("expected 2", Integer.valueOf(2).equals(jsonObject.query("/intVal")));
MyEnumClass myEnumClass = new MyEnumClass();
myEnumClass.setMyEnum(MyEnum.VAL1);
myEnumClass.setMyEnumField(MyEnumField.VAL3);
jsonObject = new JSONObject(myEnumClass);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL3", "VAL3".equals((JsonPath.read(doc, "$.myEnumField"))));
assertTrue("expected VAL1", "VAL1".equals((JsonPath.read(doc, "$.myEnum"))));
String [] names = JSONObject.getNames(myEnum);
jsonObject = new JSONObject(myEnum, names);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 3 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 3);
assertTrue("expected VAL1", MyEnum.VAL1.equals(jsonObject.query("/VAL1")));
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonObject.query("/VAL2")));
assertTrue("expected VAL3", MyEnum.VAL3.equals(jsonObject.query("/VAL3")));
names = JSONObject.getNames(myEnumField);
jsonObject = new JSONObject(myEnumField, names);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 3 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 3);
assertTrue("expected VAL1", MyEnumField.VAL1.equals(jsonObject.query("/VAL1")));
assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonObject.query("/VAL2")));
assertTrue("expected VAL3", MyEnumField.VAL3.equals(jsonObject.query("/VAL3")));
expectedStr = "{\"myEnum\":\"VAL2\", \"myEnumField\":\"VAL2\"}";
jsonObject = new JSONObject();
jsonObject.putOpt("myEnum", myEnum);
jsonObject.putOnce("myEnumField", myEnumField);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonObject.query("/myEnum")));
assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonObject.query("/myEnumField")));
JSONArray jsonArray = new JSONArray();
jsonArray.put(myEnum);
jsonArray.put(1, myEnumField);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonArray.toString());
assertTrue("expected 2 top level items", ((List>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL2", MyEnum.VAL2.equals(jsonArray.query("/0")));
assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonArray.query("/1")));
}
/**
* Wrap should handle enums exactly as a value type like Integer, Boolean, or String.
*/
@Test
public void wrap() {
assertTrue("simple enum has no getters", JSONObject.wrap(MyEnum.VAL2) instanceof MyEnum);
MyEnumField myEnumField = MyEnumField.VAL2;
JSONObject jsonObject = new JSONObject();
jsonObject.put("enum",myEnumField);
// validate JSON content
Object doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 1 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 1);
assertTrue("expected VAL2", MyEnumField.VAL2.equals(jsonObject.query("/enum")));
MyEnumClass myEnumClass = new MyEnumClass();
myEnumClass.setMyEnum(MyEnum.VAL1);
myEnumClass.setMyEnumField(MyEnumField.VAL3);
jsonObject = (JSONObject)JSONObject.wrap(myEnumClass);
// validate JSON content
doc = Configuration.defaultConfiguration().jsonProvider().parse(jsonObject.toString());
assertTrue("expected 2 top level items", ((Map,?>)(JsonPath.read(doc, "$"))).size() == 2);
assertTrue("expected VAL3", "VAL3".equals((JsonPath.read(doc, "$.myEnumField"))));
assertTrue("expected VAL1", "VAL1".equals((JsonPath.read(doc, "$.myEnum"))));
assertTrue("expecting MyEnumField.VAL3", MyEnumField.VAL3.equals(jsonObject.query("/myEnumField")));
assertTrue("expecting MyEnum.VAL1", MyEnum.VAL1.equals(jsonObject.query("/myEnum")));
}
/**
* It was determined that some API methods should be added to
* support enums:
* JSONObject.getEnum(class, key)
* JSONObject.optEnum(class, key)
* JSONObject.optEnum(class, key, default)
* JSONArray.getEnum(class, index)
* JSONArray.optEnum(class, index)
* JSONArray.optEnum(class, index, default)
*
* Exercise these enum API methods on JSONObject and JSONArray */ @Test public void enumAPI() { MyEnumClass myEnumClass = new MyEnumClass(); myEnumClass.setMyEnum(MyEnum.VAL1); MyEnumField myEnumField = MyEnumField.VAL2; JSONObject jsonObject = new JSONObject(); jsonObject.put("strKey", "value"); jsonObject.put("enumKey", myEnumField); jsonObject.put("enumClassKey", myEnumClass); // get a plain old enum MyEnumField actualEnum = jsonObject.getEnum(MyEnumField.class, "enumKey"); assertTrue("get myEnumField", actualEnum == MyEnumField.VAL2); // try to get the wrong value try { actualEnum = jsonObject.getEnum(MyEnumField.class, "strKey"); assertTrue("should throw an exception for wrong key", false); } catch (Exception ignored) {} // get a class that contains an enum MyEnumClass actualEnumClass = (MyEnumClass)jsonObject.get("enumClassKey"); assertTrue("get enum", actualEnumClass.getMyEnum() == MyEnum.VAL1); // opt a plain old enum actualEnum = jsonObject.optEnum(MyEnumField.class, "enumKey"); assertTrue("opt myEnumField", actualEnum == MyEnumField.VAL2); // opt the wrong value actualEnum = jsonObject.optEnum(MyEnumField.class, "strKey"); assertTrue("opt null", actualEnum == null); // opt a class that contains an enum actualEnumClass = (MyEnumClass)jsonObject.opt("enumClassKey"); assertTrue("get enum", actualEnumClass.getMyEnum() == MyEnum.VAL1); // opt with default a plain old enum actualEnum = jsonObject.optEnum(MyEnumField.class, "enumKey", null); assertTrue("opt myEnumField", actualEnum == MyEnumField.VAL2); // opt with default the wrong value actualEnum = jsonObject.optEnum(MyEnumField.class, "strKey", null); assertTrue("opt null", actualEnum == null); // opt with default an index that does not exist actualEnum = jsonObject.optEnum(MyEnumField.class, "noKey", null); assertTrue("opt null", actualEnum == null); /** * Exercise the proposed enum API methods on JSONArray */ JSONArray jsonArray = new JSONArray(); jsonArray.put("value"); jsonArray.put(myEnumField); jsonArray.put(myEnumClass); // get a plain old enum actualEnum = jsonArray.getEnum(MyEnumField.class, 1); assertTrue("get myEnumField", actualEnum == MyEnumField.VAL2); // try to get the wrong value try { actualEnum = jsonArray.getEnum(MyEnumField.class, 0); assertTrue("should throw an exception for wrong index", false); } catch (Exception ignored) {} // get a class that contains an enum actualEnumClass = (MyEnumClass)jsonArray.get(2); assertTrue("get enum", actualEnumClass.getMyEnum() == MyEnum.VAL1); // opt a plain old enum actualEnum = jsonArray.optEnum(MyEnumField.class, 1); assertTrue("opt myEnumField", actualEnum == MyEnumField.VAL2); // opt the wrong value actualEnum = jsonArray.optEnum(MyEnumField.class, 0); assertTrue("opt null", actualEnum == null); // opt a class that contains an enum actualEnumClass = (MyEnumClass)jsonArray.opt(2); assertTrue("get enum", actualEnumClass.getMyEnum() == MyEnum.VAL1); // opt with default a plain old enum actualEnum = jsonArray.optEnum(MyEnumField.class, 1, null); assertTrue("opt myEnumField", actualEnum == MyEnumField.VAL2); // opt with default the wrong value actualEnum = jsonArray.optEnum(MyEnumField.class, 0, null); assertTrue("opt null", actualEnum == null); // opt with default an index that does not exist actualEnum = jsonArray.optEnum(MyEnumField.class, 3, null); assertTrue("opt null", actualEnum == null); } }