I have an enum with #JsonProperty annotated:
public enum Type {
#JsonProperty("Files")
File,
#JsonProperty("Folders")
Folder,
}
I know I can deserialize a JSON string ({"fieldName":"Files"}) to get an object. But is there any way to convert the string annotated in #JsonProperty to enum value with Jackson like:
String s = "Files"
Type t = jackson.valueOf(s); // Type.File
Or can I achieve this:
Type t = Type.File;
String s = jackson.toString(t); // "Files"
I believe that a private String value can resolve this, but the code would have duplicated constants (too many "Files" and "Folders"). I wonder if Jackson or Gson has solution to achieve this.
It should just work with help of ObjectMapper
Type t = new ObjectMapper().readValue("\"Files\"", Type.class);
System.out.println(Type.File.equals(t)); //"true"
Please note that the String needs to be a valid JSON string, so it must contain the double quotes. The string content cannot be Files, but rather has to be "Files"
The other direction:
Type t = Type.File;
new ObjectMapper().writeValue(System.out, t); // "Files"
If I am understanding you correctly, then perhaps this is what you are looking for:
String annotationValueAsSTring = Type.class.getField(Type.File.name())
.getAnnotation(JsonProperty.class).value();
-- Edited --
To retrieve the Enum value from the #JsonProperty String value, then you will need to create a helper method:
public static <T extends Enum<T>> Optional<T> getEnumValueFromJsonProperty(Class<T> enumClass,
String jsonPropertyValue) {
Field[] fields = enumClass.getFields();
return Arrays.stream(fields).filter(field -> field.getAnnotation(JsonProperty.class).value().equals(jsonPropertyValue)).map(field -> Enum.valueOf(enumClass, field.getName())).findFirst();
}
Related
I would like not to define an extra type just to make the json conversion. I am using a library that needs an object as an input and then performs http operations with this data, so I cannot use a hard coded json string as input.
private static final Gson GSON = new Gson();
public static void main(String[] args) {
System.out.println(GSON.toJson(new Object() {
private String email_address = "me#mail.eu";
public String getEmail_address() {return "me#mail.eu"; }
public void setEmail_address(String mail) {email_address = mail; }
}));
}
I tried to remove getter and setter or leave the getter and remove the field but it doesn't work. Anybody knows how to fix this?
Libraries for Json serialization/deseralization like Gson, count on the fact that you have defined your custom object on which you will map the json string. This is because they use reflection on the class to map the fields with the corresponding keys in the json. Without it, it is difficult that they can achieve anything(usable).
Why not define an extra type ? We are not in the 1980s. I would personnally use a DTO. What is Data Transfer Object?
But maybe the answer to you question reside here : Java - Does Google's GSON use constructors?
I'm using google Gson to parse json and create an appropriate object:
public class Settings {
#SerializedName("version")
public String version = "1";
#SerializedName("ad_native_enabled")
public boolean nativeAdEnabled = false;
}
The problem is that I need to know if the value of nativeAdEnabled is actually parsed from json or it's the default value specified by me, i.e. does the ad_native_enabled key exist in json, or not? I've tried to use Boolean instead of boolean and just do null check, but Gson deserialisation failed. Here is the snippet of my json:
{
"status": "success",
"ad_native_enabled": false,
}
DISCLAIMER
In my situation it's not relevant and elegant to parse the json by hand and do the detection on that level(and I guess in this case I have to priorly owe the list of the keys the existence of which I want to check). It's highly desirable to somehow infer the needed information on the object level.
I described a problem on the example of a boolean, but the question may be generalised and may refer to all primitive types. So it'd be great to have a generic solution for this problem.
I know you said you already tried this, but using a Boolean field should work. I have reduced your example down a bit, and it works as expected.
I defined the Settings class like this:
public static class Settings {
#SerializedName("ad_native_enabled")
public Boolean nativeAdEnabled;
}
If you then parse JSON that contains the field:
String json = "{\"ad_native_enabled\": false}";
Settings settings = gson.fromJson(json, Settings.class);
System.out.println(settings.nativeAdEnabled); // prints false
Whereas if you parse JSON that does not contain the field:
String emptyJson = "{}";
Settings emptySettings = gson.fromJson(emptyJson, Settings.class);
System.out.println(emptySettings.nativeAdEnabled); // prints null
Did you perhaps leave the default value of the field as false? If so, the second example will print false as well. Also it seems GSON does not particularly like trailing commas after the last property in JSON objects - maybe that was why you were getting errors?
After seeing your comment, I thought a bit more about whether it is possible to somehow support default values, while still being able to tell whether the field was present in the JSON or not. The best solution I could come up with was to introduce a new wrapper type, with a custom deserializer.
I started by defining this wrapper type, which just contains the actual value of the field, and an indicator of whether this value is the default value or not:
public static class ValueWrapper<T> {
public final T value;
public final boolean isDefaultValue;
public ValueWrapper(T value, boolean isDefaultValue) {
this.value = value;
this.isDefaultValue = isDefaultValue;
}
}
The Settings class then looks like this:
public static class Settings {
#SerializedName("ad_native_enabled")
public ValueWrapper<Boolean> nativeAdEnabled = new ValueWrapper<>(false, true);
}
Here I have defined the field as having value false by default, which is why isDefaultValue is set to true.
I then defined a custom deserializer for this type. The basic idea is to take the type of ValueWrapper you are trying to deserialize, extract its generic parameter, deserialize the actual value in the JSON as the generic parameter type, and then return a new ValueWrapper where isDefaultValue is set to false. This deserializer looks like this:
public static class ValueWrapperDeserializer implements JsonDeserializer<ValueWrapper<?>> {
#Override
public ValueWrapper<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ParameterizedType parameterizedType = (ParameterizedType) typeOfT;
Type valueType = parameterizedType.getActualTypeArguments()[0];
Object value = context.deserialize(json, valueType);
return new ValueWrapper<>(value, false);
}
}
Now all we need to do is register the custom deserializer:
Gson gson = new GsonBuilder()
.registerTypeAdapter(ValueWrapper.class, new ValueWrapperDeserializer())
.create();
And we can then run through my two examples from above:
String json = "{\"ad_native_enabled\": false}";
Settings settings = gson.fromJson(json, Settings.class);
System.out.println(settings.nativeAdEnabled.value); // prints false
System.out.println(settings.nativeAdEnabled.isDefaultValue); // prints false
String emptyJson = "{}";
Settings emptySettings = gson.fromJson(emptyJson, Settings.class);
System.out.println(emptySettings.nativeAdEnabled.value); // prints false
System.out.println(emptySettings.nativeAdEnabled.isDefaultValue); //prints true
So this allows us to have the default value, but still be able to tell whether the field was set or not using isDefaultValue. This obviously has quite an impact on the API to the user of the Settings object, but perhaps this is neater than handling nulls and storing the default values elsewhere.
I'm using Jackson for creating Java objects from JSON. Let's suppose I have a JSON string like this:
{"a":"a", "b":"b", "c":"c"}
And a pojo like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class A {
private String a;
private String b;
// ...
}
So c is clearly an unknown property - and it will be left out. I was wondering, is there any way I can log that c was unknown and ignored?
I don't know of any built-in tool that does this. You can write your own with #JsonAnySetter
Marker annotation that can be used to define a non-static,
two-argument method (first argument name of property, second value to
set), to be used as a "fallback" handler for all otherwise
unrecognized properties found from JSON content.
Use it like
#JsonAnySetter
public void ignored(String name, Object value) {
// can ignore the 'value' if you only care for the name (though you still need the second parameter)
System.out.println(name + " : " + value);
}
within the class you're deserializing to, eg. your A class.
#JsonAnySetter when used on a Map field would catch all unmapped properties. When paired with #JsonAnyGetter would work for serialization and deserialization:
#JsonAnyGetter
#JsonAnySetter
private Map<String, Object> dynamicValues = new LinkedHashMap<>();
How will I be able to retrieve the value of a variable which has a dynamic name
For Example I have list of constants
public class Constant{
public static final String S_R = "Standard(240)";
public static final String S_W = "Standard(180)";
public static final String L_R = "Large(360)";
public static final String L_W = "Large(280)";
}
Based on database I build a variable name
String varName = "S" + "_" +"R"; // This can be S_R , S_W , L_R or L_W
String varVal = // How do i get value of S_R
Use a normal HashMap with variable names as strings against their values. Or use a EnumMap with enums as key and your value as values. AFAIK, that's the closest you can get when using Java. Sure, you can mess around with reflection but IMO the map approach is much more logical.
You can use a Map<String, String> and locate the value by its key.
Even better, you can have an enum:
public enum Foo {
S_R("Standard", 240),
S_W("Standard", 180),...;
private String type;
private String duration;
// constructor and getters
}
And then call Foo.valueOf(name)
(You can also do this via reflection - Constants.class.getField(fieldName) and then call field.get(null) (null for static). But that's not really a good approach.)
If you really must do this (and it's unlikely), you would have to use the Java "reflection" APIs.
I'm looking for a tool which will allow me use command-line-style (preferably POSIX) strings to initialize an object' properties and attributes.
For example, you'd provide it with String input formatted like so:
String input = "--firstName=John --MiddleName=\"Louis Victor\" --lastName=Smith";
... and it would setFirstName("John"), setMiddleName("Louis Victor") and setLastName("Smith") on a given object. (which could be a JavaBean)
Please note that the input is a single String, not an array String[] as is the case with many popular CLI argument "parsers".
This is all similar to args4j but I couldn't get that to work... and I'm hoping to avoid using #annotations.
Does anyone have code/libraries/tools which could accomplish this?
For your use case, forget regular CLI parsers, you need a custom-tailored solution. If you really have such a simple argument syntax (parameters always begin with --, no occurrences of -- in the parameter values), you can use a simple Guava-based solution like this class:
Parse the String Arguments
public class ArgParser{
// split on (optional whitespace) + "--"
private final Splitter paramSplitter = Splitter.on(
Pattern.compile("\\s*\\-{2}")).omitEmptyStrings();
// find key=value (with optional double quotes around value)
private final Pattern keyValuePattern = Pattern
.compile("(.+?)=\"?(.*?)\"?$");
public Map<String, String> getParamValues(final String posixString){
final Map<String, String> paramValues = Maps.newLinkedHashMap();
Matcher matcher;
for(final String param : paramSplitter.split(posixString)){
matcher = keyValuePattern.matcher(param);
if(!matcher.find()){
throw new IllegalArgumentException("Bad parameter: " + param);
}
paramValues.put(matcher.group(1), matcher.group(2));
}
return paramValues;
}
}
Usage
final String input =
"--firstName=John --middleName=\"Louis Victor\" --lastName=Smith";
System.out.println(new ArgParser().getParamValues(input));
Output
{firstName=John, middleName=Louis Victor, lastName=Smith}
Now you can take the map and use it with a Bean library like commons-beanutils (I prefer the Spring BeanWrapper personally, but that only makes sense if you use Spring anyway)
Define the Bean Class
Any way, I'll use this value holder class:
public class Name{
private String firstName;
private String middleName;
private String lastName;
#Override
public String toString(){
return Objects
.toStringHelper(this)
.add("first name", firstName)
.add("middle name", middleName)
.add("last name", lastName)
.toString();
}
// + getters & setters
}
Set the Bean Properties
Now we'll use BeanUtils.populate(Object, Map) to apply the parameter values, like this:
final String input =
"--firstName=John --middleName=\"Louis Victor\" --lastName=Smith";
final Map<String, String> paramValues =
new ArgParser().getParamValues(input);
final Name name = new Name();
BeanUtils.populate(name, paramValues);
System.out.println(name);
Output:
Name{first name=John, middle name=Louis Victor, last name=Smith}
Caveat: Supported Property Types
BeanUtils.populate() supports setting the following property types:
... String, boolean, int, long, float, and double.
In addition, array setters for these
types (or the corresponding primitive
types) can also be identified.
Source: BeanUtilsBean.populate(Object, Map)
If you need parameter conversion beyond that, you should probably look into using the Spring BeanWrapper after all, it's extremely powerful, has many built-in property editors and you can add custom property editors. Just change the code like this:
final Name name = new Name();
final BeanWrapper wrapper = new BeanWrapperImpl(name);
wrapper.setPropertyValues(paramValues);
Reference:
BeanWrapper
PropertyAccessor.setPropertyValues(Map)
If I understand correctly, you are looking for a Java library to parse POSIX-style command line parameters. I used JSAP some time ago and it was really cool (it was using XML configuration back then).
This
-firstName John -lastName Smith
is no POSIX, you mean
--firstName John --lastName Smith
This may be the reason, why you can't get it working.
Update:
As I look at the example, it doesn't look like it could be the reason.