Android POJO Converter:Generate scalable and bindable domain objects java class file automatically in compile time.
We always transform data between different layer in Domain-Driven Design, such as VO
, PO
, DO
, DTO
, BO
. The same situation is often encountered in the development of Android, For example, UserModelDataMapper::transform, UserEntityDataMapper::transform in Android-CleanArchitecture. The process of manually copying and converting is not only cumbersome, but also has a relatively large risk of errors. It also increases maintenance costs when adding or deleting fields. Dozer can be a good solution to this problem, but it may not be applicable on Android.
RapidOOO Can Do:
- Java classes (such as
UserVO
,UserBO
, etc.) can be automatically generated for non-reflection in compile time for the specified original POJO. - You can add configuration to the generated POJO class, add new fields (such as
gender
in User to create agenderDesc
field in the generated POJO (UserVO), and coexist with the originalgender
class and data binding) - You can perform special conversions for fields by specifying
conversionMethodName
,inverseConversionMethodName
. similar to@BindingMethod
inDatabinding
. - Chain POJO generation, such as generating
UserDO
fromUser
,UserBO
fromUserDO
,UserVO
fromUserBO
... - The conversion method
UserBo.create(User user)
,userBo.toUser()
in generated class will be automatically generate. - Support POJO
extend
. - Support Object Pool(Such as
android.support.v4.util.Pools
).
Gradle Check Newest Version
implementation "com.github.wangjiegulu:rapidooo-api:x.x.x"
annotationProcessor "com.github.wangjiegulu:rapidooo-compiler:x.x.x"
Here are two examples:
User POJO:
public class User implements Serializable {
private Long userId;
private String username;
private String nickname;
private Integer age;
private Integer gender;
// getter setter
}
Pet POJO:
public class Pet {
private Long petId;
private String petName;
private boolean isCat;
private boolean delete;
private Boolean isDog;
private Boolean clear;
private User owner;
// getter settter
}
Create BOGenerator
class, Configure the following annotation:
@OOOs(suffix = BOGenerator.BO_SUFFIX, ooos = {
@OOO(id = "user_bo_id", from = User.class, suffix = BOGenerator.BO_SUFFIX_USER),
@OOO(from = Pet.class, conversion = {
@OOOConversion(
fieldName = "owner",
targetTypeId = "user_bo_id",
targetFieldName = "ownerUser",
replace = true
)
})
})
public class BOGenerator {
public static final String BO_SUFFIX = "BO";
public static final String BO_SUFFIX_USER = "_BO";
}
Using the @OOOs
annotation to configure conversions. The @OOO
annotations explicitly specify which classes need to be converted.
@OOO(id = "user_bo_id", from = User.class, suffix = BOGenerator.BO_SUFFIX_USER)
The above represents a class conversion:
- **id:**Represent the id of this transformation, can be an arbitrary string (unique), and the default does not set the id.
- **from:**required. Represent the conversion source. from the
User
conversion. - **suffix:**Represent the name of the generated POJO class suffix, here is
_BO
, so the generated class name isUser_BO
, by default it usessuffix
in@OOOs
.
@OOO(from = Pet.class, conversion = {
@OOOConversion(
fieldName = "owner",
targetTypeId = "user_bo_id",
targetFieldName = "ownerUser",
replace = true
)
})
The above also represents a class conversion, but a field can be added with @OOOConversion
:
- **fieldName:**Specifying a new field is derived from which field of the source POJO is converted.
- **targetTypeId:**Used to specify the type id of the new field. It needs to be consistent with the
id
specified by the other@OOO
; it is also possible to specify the Class type viatargetType
. - **targetFieldName:**Specify the name of the new field, which can be arbitrary.
- **replace:**Whether the new field replaces the original field (fieldName), if false, coexist.
Following code will be automatically generated in compile time:
public class User_BO implements Serializable {
private Long userId;
private String username;
private String nickname;
private Integer age;
private Integer gender;
// getter setter
public static User_BO create(User user) {
User_BO user_BO = new User_BO();
user_BO.userId = user.getUserId();
user_BO.username = user.getUsername();
user_BO.nickname = user.getNickname();
user_BO.age = user.getAge();
user_BO.gender = user.getGender();
return user_BO;
}
public User toUser() {
User user = new User();
user.setUserId(userId);
user.setUsername(username);
user.setNickname(nickname);
user.setAge(age);
user.setGender(gender);
return user;
}
}
public class PetBO {
private Long petId;
private String petName;
private boolean isCat;
private boolean delete;
private Boolean isDog;
private Boolean clear;
private User_BO ownerUser;
// getter setter
public static PetBO create(Pet pet) {
PetBO petBO = new PetBO();
petBO.petId = pet.getPetId();
petBO.petName = pet.getPetName();
petBO.isCat = pet.isCat();
petBO.delete = pet.isDelete();
petBO.isDog = pet.getDog();
petBO.clear = pet.getClear();
petBO.ownerUser = User_BO.create(pet.getOwner());
return petBO;
}
public Pet toPet() {
Pet pet = new Pet();
pet.setPetId(petId);
pet.setPetName(petName);
pet.setCat(isCat);
pet.setDelete(delete);
pet.setDog(isDog);
pet.setClear(clear);
pet.setOwner(ownerUser.toUser());
return pet;
}
}
Create VOGenerator
as follows:
@OOOs(suffix = VOGenerator.VO_SUFFIX, fromSuffix = BOGenerator.BO_SUFFIX, ooosPackages = {
VOGenerator.PACKAGE_BO
}, ooos = {
@OOO(id = "user_vo_id", from = User_BO.class),
@OOO(from = User_BO.class/*, suffix = VOGenerator.VO_SUFFIX_USER*/,
fromSuffix = BOGenerator.BO_SUFFIX_USER,
conversion = {
@OOOConversion(
fieldName = "gender",
targetFieldName = "genderDesc",
targetType = String.class,
conversionMethodName = "conversionGender",
inverseConversionMethodName = "inverseConversionGender",
replace = false
),
@OOOConversion(
fieldName = "age",
targetFieldName = "ageDes",
targetType = String.class,
conversionMethodName = "conversionAge",
conversionMethodClass = AgeConversion.class,
replace = true
)
}
),
@OOO(from = PetBO.class,
conversion = {
@OOOConversion(
fieldName = "ownerUser",
targetFieldName = "ownerUser",
targetTypeId = "user_vo_id",
replace = true
)
}
)
})
public class VOGenerator {
public static final String VO_SUFFIX = "VO";
// public static final String VO_SUFFIX_USER = "_VO";
public static final String PACKAGE_BO = "com.wangjiegulu.rapidooo.depmodule.bll.xbo";
public static String conversionGender(Integer gender) {
if (null == gender) {
return "unknown";
}
switch (gender) {
case 0:
return "female";
case 1:
return "male";
default:
return "unknown";
}
}
public static Integer inverseConversionGender(String genderDesc) {
switch (genderDesc) {
case "male":
return 1;
case "female":
return 0;
default:
return -1;
}
}
}
The @OOOs
annotation is also used to specify the class to be generated, but here ooosPackages
is used to specify which classes under the package need to be converted.
The conversion source is generated above:User_BO
and PetBO
, and the generated classes are UserVO
and PetVO
.
Extended two fields in UserVO
:
@OOOConversion(
fieldName = "gender",
targetFieldName = "genderDesc",
targetType = String.class,
conversionMethodName = "conversionGender",
inverseConversionMethodName = "inverseConversionGender",
replace = false
)
Extend genderDesc
(used for display on View) from the gender
field of the conversion source, of type String
, and replace = false
(gender
coexists with genderDesc
):
- **conversionMethodName:**Specify the conversion method from
gender
togenderDesc
. The default is not set. - **inverseConversionMethodName:**Specify the inverse conversion method from
genderDesc
togender
. Default is not set.
NOTE: When use
conversionMethodName
andinverseConversionMethodName
to specify method names, the method signature must satisfy one of the following:
public static [Conversion target type] conversionXxx([Conversion source field type] param)
public static [Conversion target type] conversionXxx([Conversion source class type] param1, [Conversion source field type] param2)
For example, the transformation method betweengender
andgenderDesc
on top:public static String conversionGender(UserVO userVO, Integer gender)
public static Integer inverseConversionGender(String genderDesc)
By setting the above two methods, the gender
and genderDesc
fields will be bind to each other, change one of the fields, and the other field will automatically change.
@OOOConversion(
fieldName = "age",
targetFieldName = "ageDes",
targetType = String.class,
conversionMethodName = "conversionAge",
conversionMethodClass = AgeConversion.class,
replace = true
)
UserVO
also extends an ageDesc
field (replaces the age
field, does not coexist) from the age
of the conversion source and specifies conversionMethodName
, but the conversion method is not in the VOGenerator
class, and it is in the AgeConversion
class, so you need to explicitly specify conversionMethodClass
.
- **conversionMethodClass:**The class where the conversion method is , which is not set by default, in the current
Generator
class.
In addition PetVO
extends ʻownerUser` field.
The final compiled code is as follows:
public class UserVO implements Serializable {
private Long userId;
private String username;
private String nickname;
private String ageDes;
private Integer gender;
private String genderDesc;
// getter setter
public void setGender(Integer gender) {
this.gender = gender;
this.genderDesc = VOGenerator.conversionGender(gender);
}
public void setGenderDesc(String genderDesc) {
this.genderDesc = genderDesc;
this.gender = VOGenerator.inverseConversionGender(genderDesc);
}
public static UserVO create(User_BO user_BO) {
UserVO userVO = new UserVO();
userVO.userId = user_BO.getUserId();
userVO.username = user_BO.getUsername();
userVO.nickname = user_BO.getNickname();
userVO.ageDes = AgeConversion.conversionAge(user_BO.getAge());
userVO.gender = user_BO.getGender();
userVO.genderDesc = VOGenerator.conversionGender(user_BO.getGender());
return userVO;
}
public User_BO toUser_BO() {
User_BO user_BO = new User_BO();
user_BO.setUserId(userId);
user_BO.setUsername(username);
user_BO.setNickname(nickname);
// Loss field:age, recommend to use `inverseConversionMethodName`.
user_BO.setGender(gender);
return user_BO;
}
}
NOTE: Above
User_BO
, because of theage
field isreplace
, and onlyconversionMethodName
is set,inverseConversionMethodName
is not set, so theage
value is lost when thetoUser_BO()
method is inverse converted. It is recommended to useinverseConversionMethodName
.
public class PetVO {
private Long petId;
private String petName;
private boolean isCat;
private boolean delete;
private Boolean isDog;
private Boolean clear;
private UserVO ownerUser;
// getter setter
public static PetVO create(PetBO petBO) {
PetVO petVO = new PetVO();
petVO.petId = petBO.getPetId();
petVO.petName = petBO.getPetName();
petVO.isCat = petBO.isCat();
petVO.delete = petBO.isDelete();
petVO.isDog = petBO.getDog();
petVO.clear = petBO.getClear();
petVO.ownerUser = UserVO.create(petBO.getOwnerUser());
return petVO;
}
public PetBO toPetBO() {
PetBO petBO = new PetBO();
petBO.setPetId(petId);
petBO.setPetName(petName);
petBO.setCat(isCat);
petBO.setDelete(delete);
petBO.setDog(isDog);
petBO.setClear(clear);
petBO.setOwnerUser(ownerUser.toUser_BO());
return petBO;
}
}
Copyright 2018 Wang Jie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.