I am trying to map this object
public class Source {
private String value1;
private String value2;
private String value3;
}
Into this object
public class Target {
private String targetValue1;
private String targetValue2;
private String targetValue3;
}
This is the Mapper definition.
#Mapper
public interface SourceMapper {
void toTarget(Source source, #MappingTarget Target target);
}
What I am trying to achieve is only map fields in source into target only when the fields in target are null. For example, source.value1 only maps to target.targetValue1 when target.targetValue1 is null. If it is not null, the mapping for that field is ignored.
Is it possible with MapStruct without having to write custom code?
Edit
I changed the field names of Target to make it clear that the names of the Target may/may not match the names of the fields in Source.
I don't think that can be done with mapstruct. If you still want to use mapstruct, you can ignore the target variables that could be null with #Mapping (target =" propName ", ignore = true) and decide yourself with a #AfterMapping method when you set your target variables.
You can achieve that by doing the following trick:
first, you need to know that you can control the mapping behavior on 'source' nulls fields
using Mapping.nullValuePropertyMappingStrategy()
So the following should do the work:
Target target = new Target(); // the actual target, first grade values
Source source = new Source(); // the actual source, secund grade values
Target result = new Target(); // the result
SourceMapper.toTarget(source, result); // now you have secund grade value
SourceMapper.toTarget(target, result); /* now you overide the secund grade value only
if the first grade isn't null */
#Mapper
public interface SourceMapper {
#Mapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void toTarget(Target source, #MappingTarget Target target);
void toTarget(Source source, #MappingTarget Target target);
}
Related
I have a dictionaries which has multiple fields like: id, code, ruName, enName. id is a UUID, the others are Strings.
What I want is something like that:
#Mapping(source = "sourceName", target = "targetName", dictionary = "dictName", dictionaryField = "dictionaryField")
and based on target type it will generate something like that
if target type UUID
return target.targetName(getId(dictionary ,dictionaryField , sourceName));
if target type String
return target.targetName(getValue(dictionary, dictionaryField, sourceName));
What I have now is a generator which generates mappers for every dictionary and for every field in format dictionaryByFieldName, so I can use this format:
#Mapping(source="sourceName", target="targetName", qualifiedByName = "dictionaryByFieldName")
But I don't like it cos most of created mappers have no uses in project and aren't valid cos not every field is unique to get id by field-_-
Currently it is not possible to retrieve the fieldname within mapstruct, what however is possible is using an #Mapping per field to minimize the amount of mapping code.
for example:
#Mapping( target = "myUuidFieldName", expression = 'java(dict.getId("myUuidFieldName", source.getMyUuidFieldName()))' )
#Mapping( target = "myStringFieldName", expression = 'java(dict.getValue("myStringFieldName", source.getMyStringFieldName()))' )
Target map(Source source, #Context Dictionary dict);
and have a separate class called Dictionary in which you store the mappings for retrieval. This way you can easily replace the Dictionary with another Dictionary implementation in case you need a different translation.
Example of a Dictionary class:
private class Dictionary{
DbRecordAccessor dbRecord;
Map<String, RetrievalInformation> retrievalMap;
// constructor and retrievalMap initialization methods
UUID getId(String fieldName, String value){
RetrievalInformation info = retrievalMap.get(fieldName);
return dbRecord.getId(info.getDictionaryName(), fieldName, info.getDictionaryField());
}
String getValue(String fieldName, String value){
RetrievalInformation info = retrievalMap.get(fieldName);
return dbRecord.getValue(info.getDictionaryName(), fieldName, getId(fieldName, value));
}
}
The following is not (yet) supported by mapstruct. See here for more information.
It would be nice if you could have done the following:
Target map(Source source, #Context Dictionary dict);
UUID getId(String value, #TargetProperty String targetField, #Context Dictionary dict) {
return dict.getId(targetField, value);
}
I want to map field from Source to Target class, and if the source value is null, I would like to convert it to default value based on the data type ("" for strings, 0 for numeric types etc.). For setting the values, I am not using regular setters, but builder (with protobuf, so the names of the methods is newBuilder() and build()).
class Source {
private final String value; // getter
}
class Target {
private final String value;
public static Builder newBuilder() {return new Builder()}
public static class Builder {
public static setValue() {/*Set the field*/}
public static Target build() {/*Return the constructed instance*/}
}
My mapper looks like this:
#Mapper(
nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT,
nullValueMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT
)
public interface TargetMapper {
Target map(Source source);
}
The generated mapper implementation with this code calls target.setValue(source.getValue()), instead of performing the null check and setting default value if source returns null. The interesting part is when I add the following annotation to the map method, the null check is present in the implementation.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
Is this a bug in MapStruct with builders, or am I missing some configuration to be ably to set the null mapping as a default policy, instead of duplicating it on all field mappings?
EDIT: For some reason, adding nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS to the class level #Mapper annotation adds the null check, but does not explicitly set the value, just skips the call to setValue. For protobuf, this is okay, since this functionality is in the library, but for other implementations the field would remain null.
#Mapping(source="value", target="value", nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.SET_TO_DEFAULT)
applies to update method (so methods that have the #MappingTarget annotated parameter
There's no real counterpart for regular methods:
1. NullValueMappingStragegy applies to the bean argument itself.
2. NullValueCheckStragegy does perform a check on bean properties, but does not return a default.
Naming is not really brilliant and it has a long history. We still have the intention to align this one day.
A solution would be to use an Object factory creating the builder target object and pre-populate it with default values and then let MapStuct override these one day.
Perhaps you could do something like this:
#Mapper(
// to perform a null check
nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS
)
public interface TargetMapper {
Target map(Source source);
}
// to create a pre-defined object (defaults set a-priori). Not sure
// whether this works with builders.. just try
#ObjectFactory
default Target.Builder create() {
Target.Builder builder = Target.newBuilder();
builder.setValueX( "someDefaultValue" );
return builder;
}
How can I inject a map into an object using only Core Java?
I have a map with 4 key-value(String, Object) pairs and a class with 3 fields, I want to invoke the setter method based on the key name and set them.
{
"variableA": "A",
"variableB": true,
"variableC": 1,
"variableD": "DONT USE"
}
public Class Example {
public void setVaraibleA(String variableA);
public void setVaraibleB(Boolean variableB);
public void setVaraibleC(Integer variableC);
}
Example example = new Example();
// Do something to map it
assert(example.getVariableA.equals("A"));
assert(example.getVariableB.equals(true));
assert(example.getVariableC.equals(1));
you can use Java Reflection to get a method (given its name) and invoke it with a given parameter.
Example example = new Example();
Method method = Example.class.getMethod("setVariableA", String.class);
method.invoke(example, "parameter-value1");
Alternatively to #BeppeC's answer, if you can't easily determine the type of the object that you're injecting at runtime, and assuming that you don't have duplicate property names, I would use Class's getMethods() method and Method's getName() method.
Basically, I would write some code like the following:
Method[] exampleMethods = Example.class.getMethods();
Map<String, Method> setterMethodsByPropertyName = new HashMap<>(exampleMethods.length);
for (Method exampleMethod : exampleMethods) {
String methodName = exampleMethod.getName();
if (!methodName.startsWith("set")) {
continue;
}
// substring starting right after "set"
String variableName = methodName.substring(3);
// use lowercase here because:
// 1. JSON property starts with lower case but setter name after "set" starts with upper case
// 2. property names should all be different so no name conflict (assumption)
String lcVariableNmae = variableName.toLowerCase();
setterMethodsByPropertyName.put(lcVariableName, exampleMethod);
}
// later in the code, and assuming that your JSON map is accessible via a Java Map
for (Map.Entry<String, ?> entry : jsonMap.entrySet()) {
String propertyName = entry.getKey();
String lcPropertyName = propertyName.toLowerCase();
if(!setterMethodsByPropertyName.containsKey(lcPropertyName)) {
// do something for this error condition where the property setter can't be found
}
Object propertyValue = entry.getValue();
Method setter = setterMethodsByPropertyName.get(lcPropertyName);
setter.invoke(myExampleInstance, propertyValue);
}
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
Summarizing excellent answers by Programmer Bruce and StaxMan:
Missing properties referenced by the constructor are assigned a default value as defined by Java.
You can use setter methods to differentiate between properties that are implicitly or explicitly set. Setter methods are only invoked for properties with explicit values. Setter methods can keep track of whether a property was explicitly set using a boolean flag (e.g. isValueSet).
What happens if I annotate a constructor parameter using #JsonProperty but the Json doesn't specify that property. What value does the constructor get?
For questions such as this, I like to just write a sample program and see what happens.
Following is such a sample program.
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFoo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper();
// {"name":"Fred","id":42}
String jsonInput1 = "{\"name\":\"Fred\",\"id\":42}";
Bar bar1 = mapper.readValue(jsonInput1, Bar.class);
System.out.println(bar1);
// output:
// Bar: name=Fred, id=42
// {"name":"James"}
String jsonInput2 = "{\"name\":\"James\"}";
Bar bar2 = mapper.readValue(jsonInput2, Bar.class);
System.out.println(bar2);
// output:
// Bar: name=James, id=0
// {"id":7}
String jsonInput3 = "{\"id\":7}";
Bar bar3 = mapper.readValue(jsonInput3, Bar.class);
System.out.println(bar3);
// output:
// Bar: name=null, id=7
}
}
class Bar
{
private String name = "BLANK";
private int id = -1;
Bar(#JsonProperty("name") String n, #JsonProperty("id") int i)
{
name = n;
id = i;
}
#Override
public String toString()
{
return String.format("Bar: name=%s, id=%d", name, id);
}
}
The result is that the constructor is passed the default value for the data type.
How do I differentiate between a property having a null value versus a property that is not present in the JSON?
One simple approach would be to check for a default value post deserialization processing, since if the element were present in the JSON but had a null value, then the null value would be used to replace any default value given the corresponding Java field. For example:
import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
import org.codehaus.jackson.annotate.JsonMethod;
import org.codehaus.jackson.map.ObjectMapper;
public class JacksonFooToo
{
public static void main(String[] args) throws Exception
{
ObjectMapper mapper = new ObjectMapper().setVisibility(JsonMethod.FIELD, Visibility.ANY);
// {"name":null,"id":99}
String jsonInput1 = "{\"name\":null,\"id\":99}";
BarToo barToo1 = mapper.readValue(jsonInput1, BarToo.class);
System.out.println(barToo1);
// output:
// BarToo: name=null, id=99
// {"id":99}
String jsonInput2 = "{\"id\":99}";
BarToo barToo2 = mapper.readValue(jsonInput2, BarToo.class);
System.out.println(barToo2);
// output:
// BarToo: name=BLANK, id=99
// Interrogate barToo1 and barToo2 for
// the current value of the name field.
// If it's null, then it was null in the JSON.
// If it's BLANK, then it was missing in the JSON.
}
}
class BarToo
{
String name = "BLANK";
int id = -1;
#Override
public String toString()
{
return String.format("BarToo: name=%s, id=%d", name, id);
}
}
Another approach would be to implement a custom deserializer that checks for the required JSON elements. And yet another approach would be to log an enhancement request with the Jackson project at http://jira.codehaus.org/browse/JACKSON
In addition to constructor behavior explained in #Programmer_Bruce's answer, one way to differentiate between null value and missing value is to define a setter: setter is only called with explicit null value.
Custom setter can then set a private boolean flag ("isValueSet" or whatever) if you want to keep track of values set.
Setters have precedence over fields, in case both field and setter exist, so you can "override" behavior this way as well.
I'm thinking of using something in the style of an Option class, where a Nothing object would tell me if there is such a value or not. Has anyone done something like this with Jackson (in Java, not Scala, et al)?
(My answer might be useful to some people finding this thread via google, even if it doesn't answer OPs question)
If you are dealing with primitive types which are omittable, and you do not want to use a setter like described in the other answers (for example if you want your field to be final), you can use box objects:
public class Foo {
private final int number;
public Foo(#JsonProperty Integer number) {
if (number == null) {
this.number = 42; // some default value
} else {
this.number = number;
}
}
}
this doesn't work if the JSON actually contains null, but it can be sufficient if you know it will only contain primitives or be absent
another option is to validate the object after deserialization either manually or via frameworks such java bean validation or, if you are using spring, the spring validation support.
How to know whether a property exists or not in a property file in java?
According to http://java.sun.com/javase/6/docs/api/java/util/Properties.html, getProperty() returns null if the property was not found. You could also call propertyNames() or stringPropertyNames() and look to see whether the property name of interest is in the returned set.
Yet another alternative is to exploit the fact the Properties extends Hashtable<Object,Object> and use containsKey.
Just load the properties file and then try to get the desired property.
public String getProperty(String key)
Searches for the property with the specified key in this property list. If the key is not found in this property list, the default property list, and its defaults, recursively, are then checked. The method returns null if the property is not found.
http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#getProperty(java.lang.String)
You can also call getProperty(String key, String defaultValue) and check if the return value is the defaultValue.
See https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html#getProperty-java.lang.String-java.lang.String-
You can use hasProperty
AllValues.hasProperty("childList")
If you want to check that at the start of program you can do the following:
Create a class VerifiedProperties that extends Properties
Add all properties as fields of this class as public final int/String/boolean/etc...
Add private final String propertyNotValid="Property not valid" String to this class
Add private final String propertyNotFound="Property not found" String to this class
Override getProperty() method from Properties class.
You can add #Deprecated tag to suggest usage of the fields. It is impossible to hide this method because it is public in Properties class.
Initialize all fields in the constructor using getProperty() method or dedicated for type (look examples below)
Example methods that takes care of different property types:
#Override
#Deprecated
/*
Deprecated annotation added to suggest usage of the fields.
*/
public final String getProperty(String key)
{
String propertyValue = super.getProperty(key);
if (propertyValue != null)
{
return propertyValue;
}
else
{
throw new NoSuchFieldError(this.propertyNotFound + " " + key);
}
}
private int getIntegerProperty(String key)
{
String propertyValue = this.getProperty(key);
try
{
int propertyIntValue = Integer.parseInt(propertyValue);
return propertyIntValue;
}
catch (NumberFormatException e)
{
throw new NumberFormatException(this.propertyNotValid + " " + key);
}
}
private boolean getBooleanProperty(String key)
{
String propertyValue = this.getProperty(key);
try
{
boolean propertyBooleanValue = Boolean.parseBoolean(propertyValue);
return propertyBooleanValue;
}
catch (NumberFormatException e)
{
throw new NumberFormatException(this.propertyNotValid + " " + key);
}
}
private long getLongProperty(String key)
{
String propertyValue = this.getProperty(key);
try
{
long propertyLongValue = Long.parseLong(propertyValue);
return propertyLongValue;
}
catch (NumberFormatException e)
{
throw new NumberFormatException(this.propertyNotValid + " " + key);
}
}
Then you can create somewhere:
public static VerifiedProperties properties;
and use the properties that you need as properties.myProperty
Advantages:
you have full control over properties which includes exception handling and null checking
If property does not exist or is in incorrect format, you will have the information at the initialization of the program
You don't need to worry about parsing properties to different types than String in your code.
You can add validators to your String properties
You can easily refactor property name
If you are using external property file that can be modified by the user outside of your application, if provided change is incorrect or there are fields missing your program will not start.
Disadvantages:
For each property besides adding value to *.properties file you need to create field and assign value in the constructor. If you have a lot of properties then this file can look unpleasant.
Hints:
it is easier to mantain the file if you keep the same name for the field as in properties file.
(Netbeans) you can Toggle Rectangular Selection to add public final String and similar to many lines at once.
(Netbeans) to keep *.properties file clean you can use this solution.
The answer by crazyscot is now outdated. According to the new javadoc, the property will just get created if it doesn't exist,
"If there is no current set of system properties, a set of system properties is first created and initialized in the same manner as for the getProperties method".
Here is some trick how to find out is some file (not mandatory property file) exists in class path
public class FileUtil {
public static boolean isFileExists(String fileName){
return null != FileUtil.class.getResourceAsStream(fileName);
}
}
Sure it not always works as long it depends on class loading aspects