Simple question/problem for anybody familiar with building APIs... I have many objects that I prefer to represent as a string rather than a Json object, for simplicity purposes.
For example, I have a date range which I could (and used to) place into an object (with start end end date members), but considering we can have multiple of these ranges, I could instead have this...
['20130210-20130315','20130520-20130524']
Which IMO looks a lot simpler and cleaner than
[
{
"start":"2013-02-10",
"end":"2013-03-15"
},
{
"start":"2013-05-20",
"end":"2013-05-24"
}
]
And this holds for various other objects which are in the main Json object for the service.
My dilemma of just treating them as Strings is that then I lose the ability to mark them with interfaces, which are used all throughout the code. (For instance, this Json in particular might be marked with a "Filter" interface which many methods take in.)
That said, is there any way to satisfy both of these conditions, i.e. having a custom Json object (implementing my own interfaces, etc.) AND have Jackson parse it like a String primitive? I'm hoping this can be accomplished without much work involving custom serialization & deserialization, since I have lots of objects.
Hate duplicating posts, so in an attempt to add some value here -- this does exactly what I want with arrays --
public class MyAwesomeJson extends JacksonObject implements S {
private final String value;
public MyAwesomeJson(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
}
Then to get the array form --
public class MyAwesomeJsonArray extends JacksonObject implements A {
private final Set<MyAwesomeJson> values = Sets.newLinkedHashSet();
public MyAwesomeJsonArray(MyAwesomeJson... values) {
this.values.addAll(Arrays.asList(values));
}
#JsonValue
public Set<MyAwesomeJson> getValues() {
return values;
}
}
System.out.println(new MyAwesomeJsonArray(new MyAwesomeJson("Yellow"),
new MyAwesomeJson("Goodbye")));
["Yellow","Goodbye"]
Related
I'm working on a configuration system. I'd like to be able to load config values from a JSON file and have them "automagically" convert to the Java type I need. I'm using Jackson for the JSON parsing. For primitive types like floats and strings, it's no big deal, but I'm running into a snag with enums.
Let's say I have the following enum:
public enum SystemMode
{
#JsonProperty("Mode1")
MODE1("Mode1"),
#JsonProperty("Mode2")
MODE2("Mode2"),
#JsonProperty("Mode3")
MODE3("Mode3");
private final String name;
private SystemMode(String name)
{
this.name = name;
}
#Override
#JsonValue
public String toString()
{
return this.name;
}
}
Now, let's say I want to represent a list of values of this enum for a given config variable using the following JSON representation:
{
"Project" : "TEST",
"System" : {
"ValidModes" : ["Mode1", "Mode2"]
}
}
And I'd like to be able to do something like the following:
ArrayList<SystemMode> validModes = (ArrayList<SystemMode>) configurator.getConfigValue("/System/ValidModes");
For reference, my configurator class's getConfigValue method is essentially a thin wrapper over the Jackson JSON parsing:
public Object getConfigValue(String JSON_String)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(JSON_String);
return objectMapper.convertValue(node, Object.class);
}
(The real method has some exception checking that has been omitted for clarity).
Now, when I call the above, Jackson correctly deduces that I want an ArrayList and fills it. However, instead of getting an ArrayList of SystemMode enums, I get an ArrayList of Strings and immediately throw an exception when I attempt to use the list. I have tried several different ways of representing the data to no avail. It seems no matter what I try, Jackson wants to return a list of strings instead of a list of enums.
So my question is this:
How can I make Jackson (version 2.9.4) JSON properly deserialize a list of enum values in a way that is compatible with my single "Object getConfigValue()" method?
The following will provide the correct binding for your enum.
public List<SystemMode> getConfigValue(String path)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
return objectMapper.convertValue(node, new TypeReference<List<SystemMode>>(){});
}
The second option is to convert the list of String yourself, for example:
List<SystemMode> result = jsonResult.stream().map(SystemMode::valueOf).collect(Collectors.toList());
Third option:
public <T>List<T> getConfigValue(String path, Class<T> type)
{
JsonNode node = JsonNodeFactory.instance.objectNode().at(path);
CollectionType toType =
objectMapper.getTypeFactory().constructCollectionType(List.class, type);
return objectMapper.convertValue(node, toType);
}
I am developing a REST webservice using the Play Framework 2.5 (Java) and the form data binding (from Spring Framework).
I am quite experienced with this API and like the way it formalizes validation constraints (e.g. Required), so I would like to avoid using the BodyParser API.
I need to parse a JSON request such as this :
{
"elements": [
{
"val": "1"
},
{
"val": ["1","2","3"]
}
]
}
The problem is that "val" accepts two different types : a string (java.lang.String in Java) and an array of strings (java.util.List in my code).
How could I "typesafely" model such a JSON form in my Java code ?
I have already tried to use an abstract (and generic) class implemented by two different subclasses with different types for the "val" attribute, but Spring fails to instantiate the object (BeanInstantiationException).
Here is the current data model :
public class Foo {
#Constraints.Required
public List<Fii> elements;
}
public class Fii {
#Constraints.Required
// Which type ? Object ?
public ? val;
}
public class Response
{
List<ResponseEntry> response;
/*getters + setters */
public static class ResponseEntry
{
private List<Value> elements;
/*setters + getters*/
public static class Value
{
private List<Object> val;
}
}
}
Unfortunately, with the structure of the JSON you are handling, the only way to deserialize it is to have the value attribute be type Object. However, once the JSON is deserialized, you can easily figure out whether value is an object or a single value.
Notice that JSON only supports five data types: objects (Map in java), arrays, strings, numeric and boolean. It looks like in your case, value would most likely be either a number or a map of numbers; then you have two possibilities to check for. Using a quick instanceof comparison, you should be able to figure out what type of value it is.
ObjectMapper mapper = new ObjectMapper();
Response r = mapper.readValues(json, Response.class);
Value val = r.response.get(0).values.get(0);
if (val.value instanceof Map)
; // multiple
else
; // single
I am currently processing a huge data input, including a lot of values, which I want to receive in getters for later use.
After writing a few methodes, I wondered if it might be a better idea to just use one get Method, with an enum-class containing all possible values, e.g.
public double getStat(StatType st) {
if(st != null)
return st.getValue();
}
instead of
public double getAvgReflection() {
return ...
}
public double getMaxLifesteal() {
return ...
}
Is there any convention for using either of the two possibilities?
Any dis/advantages?
Thanks in advance!
Using an Enum maxes it easier to add new stats, just add a constant in the Enum.
But all stats need to behave the same way, i.e. be doubles in your example. What happens if you want to add an int stat?
The "convention" you are asking about really boils down to the use and definition of your values. When you make the values Enums, then you must handle them as that type. Meaning, the fields in your class would have to be defined as such:
private MyEnum avgReflection;
private MyEnum maxLifesteal;
...
public MyEnum getAvgReflection {
return this.avgReflection;
}
And so forth.
Now, you could have your Enums return double values, but these values are static. I don't think you are concerned about static values, but, instead, perhaps a static set of values.
You then have two possible options: declare all possible parameters as fields, or create one aggregate field to hold all values and then use an Enum as an index:
public enum MyEnum {
averageReflection(0),
maximumLifeSteal(1);
private int value;
private MyEnum(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
...
private double[] attributes = new double[100]; // arbitrary initialization
public double getAttribute(MyEnum attribute) {
return this.attributes[attribute.getValue()];
}
The two restrictions on using an array (assuming you want primitive values and you are concerned about performance) is that all the values must be the same type, and the number of attributes will be set at compile time.
Additionally, you may just want to use a Map<String,double> or Map<MyEnum,double>, or even Map<MyEnum, Object>. A Map type will give you the ability to maintain a dynamically-sized set and possibly holding multiple types as well (with the costly overhead of converting your values).
You should base your decision on the amount of attributes you need to keep, the kind of overhead you are willing to tolerate, and your style.
This question already has answers here:
How to get an enum value from a string value in Java
(32 answers)
Closed 7 years ago.
Let's say I have an enum with 100 values. For simplicity's sake, take the following example:
public enum code
{
CODE_1("string1"),
CODE_2("string2"),
CODE_3("string3"),
CODE_4("string4"),
...
}
I want to create a public method to convert strings with a known format (like "string1", "string2"...) to the appropiate enum value CODE_1, CODE_2... Typically this is done by iterating over all values, and if a match is found, return that enum value. (Details can be found in this question.)
However, I'm concerned with reguraly looping over all values. Could this potentially be a huge bottleneck? What if instead of 100 element, there were 1000?
As an exercise for myself, I tried to optimize this lookup with a static map, which can assure O(1) lookup time given any string. I like this extra gimmick, but I only want to include it in my code if it is actually necessary. What are your thoughts and findings on using the iterating method vs the map method?
public enum Code
{
...
//enum values
...
//The string-to-Code map
private static final Map<String,Code> CODE_MAP = populateMap();
private static Map<String,Code> populateMap()
{
Map<String,Code> map = new HashMap<String,Code>();
for(Code c : Code.values())
{
map.put(c.getCode(), c);
}
return map;
}
private String code;
private Code(String code)
{
this.code = code;
}
public String getCode()
{
return this.code;
}
public Code convertFromString(String code)
{
//assume that the given string is actually a key value in the map
return (Code) CODE_MAP.get(code);
}
}
You want a Map<String, Code>, but how to populate it neatly? Enums don't allow you to initialize a static fields before the enum instances are initialized, but there's a neat little trick, called the Initialization-on-demand holder idiom, that makes using a statically initialized map needed for this functionality easy to implement:
public enum Code {
CODE_1("string1"),
CODE_2("string2"),
CODE_3("string3"),
// etc
;
private static class Holder {
static Map<String, Code> CODE_MAP = new HashMap<>();
}
private final String code;
private Code(String code) {
this.code = code;
Holder.CODE_MAP.put(code, this);
}
public String getCode() {
return this.code;
}
public Code convertFromString(String code) {
return Holder.CODE_MAP.get(code);
}
}
This works because the class loader initializes inner static classes before initializing the enum class, so the map is assigned ready to load during enum instance initialization.
No loops. No special code to load the map (done in constructor). Minimal code.
Map is good option : cleaner code and O(1) . If you use for-loop then the best you get is O(n)
Your provided solution is proper implementation.
As you will have to expose only one method and it is more readable.
And it is always good to use Map instead of iterating it manually.
And also as you mentioned the complexity is O(1).
+1 to your question, as it gives a cleaner approach to use enum in some usecases.
If your string code value is a known and consistent format you could avoid the use of a Map and the memory it consumes and construct the CODE enum lookup value on the fly:
public static Code convertFromString(String code) {
return valueOf("CODE_" + code.substring("string".length()));
}
Well, alternatives to the map-solution would be a giant switch-statement (could be automatically generated) or binary-searching an array containing the strings. I don't think either will beat HashMap's performance by a large margin, though if it really matters, you are probably best off by benchmarking.
One thing that has not been mentioned is how Enum.valueOf() let's you turn a String into an enum value, if it has the exact name of one of the enum members. If this is at all a possiblity in your case (looking just at your example, I don't see how Code.CODE_1 could not be easily renamed Code.string1 etc.), I would suggest using it, as it requires no additional coding at all and will hence be the most understandable.
I'm currently working on a codebase where there are a few classes of variable, like database paths, which are simply represented as Strings. Most of the operations on these (non-)types are defined in a utility class.
I have created a new class to represent a database, with operations defined as instance methods, in traditional OOP style. However it is quite laborious to go through the large codebase and refactor it to use the new types. Does anyone have any advice as to how to do this quickly and effectively?
Migrate the utility class to use your new class. Then the utility class methods should only contain two statements. One for creating your class and the other is invoking your class. After that, you can inline the utility class methods thereby eliminating the need for it.
When you are finished with that, you need to look for a way to not instantiate your new class over and over again. This should be done by refactoring the local variable to an instance field which is initialized at construction time.
Database paths sound like they should be Strings to me. What else makes sense? And they should be externalized, either in configuration files or a database. That's the least of your problems.
Persistence has been so many times over (e.g. Hibernate, Spring JDBC, iBatis, etc.) that I'd wonder how you could possibly improve on them. If you have to go to the trouble of refactoring - and you must - I'd advise using anything other than what you've done.
If you must write something, Google for "generic DAO". You'll get stuff like this:
http://www.ibm.com/developerworks/java/library/j-genericdao/index.html
If your work isn't patterned after something like that, throw it away and re-think things.
A technique I've used in C# (and just ported to Java - apologies if I've made an error, I'm new to Java) is to create StringlyTyped classes, e.g. a base class
public abstract class StringlyTyped {
private String value;
public StringlyTyped (String value){
if (value == null){
throw new IllegalArgumentException("value must not be null");
}
this.value = value;
}
public String getValue() { return value; }
#Override
public boolean equals(Object other){
if (other == this) {
return true;
}
if (other == null || !this.getClass().equals(other.getClass())){
return false;
}
StringlyTyped o = (StringlyTyped)other;
return o.getValue().equals(this.getValue());
}
#Override
public int hashCode(){ return this.getValue().hashCode(); }
#Override
public String toString() { return this.getValue(); }
}
Then derived class
public class ProviderName extends StringlyTyped {
public ProviderName(String value) {
super(value);
}
}
And usage
public void Foo(ProviderName provider) {
}
It makes sense when you have methods with many String parametrers, e.g. you can avoid
public void Foo(String username, String password, String db, String table, String constraint)
and instead have code that is strongly typed like this:
public void Foo(UserName username, Password password, DatabasePath db, TableName table...)
etc...
I generally try to isolate the strings at the limit of the application/process boundary, such as when they are retrieved from a database or received via a web operation.
At that application/process boundary is often the ideal place to map/convert/deserialize the strings into a more proper object model, as supported by whatever language you are using.
Similarly, the object model can be mapped/converted/serialized back into string form as it exits your application/process boundary.
It is worth noting that this stringly typing can be somewhat subtle. I commonly see xml intruding into application and domain layers. A similar example from the .NET space would be failing to map ADO.NET DataTables (with their string column names and untyped field values) into classes/objects pretty much as soon as they are received. I have no doubt that there are many similar equivalents in the Java world. Stringly Typing is not just limited to date values, as the joke goes.