LoginSignup
5
7

More than 3 years have passed since last update.

JAVAオブジェクトマッピングライブラリ

Last updated at Posted at 2020-06-28

はじめに

Beanの変換に詰め替え作業は手動で実施するとコード量は増えてしまい、メンテナンス性は良くないです。
BeanUtils.copyPropertiesのような便利メソッドがありますが、ほかのマッピングライブラリをまとめてみます。

FasterXML ObjectMapper

JSON関連する処理はこれを使うと便利です。

使い方

build.gradleに追加

build.gradle
// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.9.8'

Beanクラス作成

package com.test.lombok;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
    private String userId;

    private String userName;

    private String age;

    private String gender;
}

user.jsonサンプル作成

user.json
{
"userId": "user001",
"userName": "テスト 太郎",
"age": 20,
"gender": "famale"
}

テストコード

package com.test.lombok;

import java.io.File;
import java.util.Map;

import com.fasterxml.jackson.databind.ObjectMapper;

public class TestMain {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        // ファイルパスで指定
        User user1 = mapper.readValue(
                new File("C:\\workspace_sts\\lombok\\src\\main\\java\\com\\test\\lombok\\user.json"), User.class);
        System.out.println(user1);

        // URL形式で指定
        User user2 = mapper.readValue(TestMain.class.getResource("user.json"), User.class);
        System.out.println(user2);

        // 文字列から変換
        User user3 = mapper.readValue(
                "{\"userId\":\"user001\", \"userName\": \"テスト 太郎2\", \"age\": 22, \"gender\": \"male\"}", User.class);
        System.out.println(user3);

        // オブジェクトから文字列に変換
        String jsonString = mapper.writeValueAsString(user3);
        System.out.println(jsonString);

        // 文字列からMapに変換
        Map<String, String> userMap = mapper.readValue(jsonString, Map.class);
        System.out.println(userMap);

    }

}

結果:

User(userId=user001, userName=テスト 太郎, age=20, gender=famale)
User(userId=user001, userName=テスト 太郎, age=20, gender=famale)
User(userId=user001, userName=テスト 太郎2, age=22, gender=male)
{"userId":"user001","userName":"テスト 太郎2","age":"22","gender":"male"}
{userId=user001, userName=テスト 太郎2, age=22, gender=male}

ModelMapper

使い方

build.gradleに追加

build.gradle
    // https://mvnrepository.com/artifact/org.modelmapper/modelmapper
    compile group: 'org.modelmapper', name: 'modelmapper', version: '2.3.5'

元Beanクラス

package com.test.lombok;

import lombok.Data;

@Data
public class User {
    private String userId;

    private String userName;

    private String age;

    private String gender;

    private Address addr;
}

@Data
class Address {
    private String zipCode;
    private String address1;
    private String address2;
    private String address3;
}

DTOクラス

package com.test.lombok;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class UserDTO {
    private String userId;

    private String userName;

    private String age;

    private String gender;

    private String addrZipCode;

    private String addrAddress1;

    private String addrAddress2;

    private String addrAddress3;
}

テストコード

package com.test.lombok;

import org.modelmapper.ModelMapper;

public class TestMain {

    public static void main(String[] args) throws Exception {
        ModelMapper modelMapper = new ModelMapper();

        User user = new User();
        user.setUserId("user001");
        user.setUserName("テスト");
        user.setAge("18");
        user.setGender("male");
        Address addr = new Address();
        addr.setZipCode("984-0031");
        addr.setAddress1("○○県");
        user.setAddr(addr );

        UserDTO userDTO = modelMapper.map(user, UserDTO.class);
        System.out.println(userDTO);

    }

}

結果:

UserDTO(userId=user001, userName=テスト, age=18, gender=male, addrZipCode=984-0031, addrAddress1=○○県, addrAddress2=null, addrAddress3=null)

明示的にマッピング(Explicit Mapping)

package com.test.lombok;

import org.modelmapper.ModelMapper;
import org.modelmapper.TypeMap;

public class TestMain {

    public static void main(String[] args) throws Exception {
        ModelMapper modelMapper = new ModelMapper();

        User user = new User();
        user.setUserId("user001");
        user.setUserName("テスト");
        user.setAge("18");
        user.setGender("male");
        Address addr = new Address();
        addr.setZipCode("984-0031");
        addr.setAddress1("○○県");
        user.setAddr(addr);

        TypeMap<User, UserDTO> typeMap = modelMapper.typeMap(User.class, UserDTO.class).addMappings(mapper -> {
            mapper.map(src -> src.getAddr().getZipCode(), UserDTO::setAddrZipCode);
            mapper.map(src -> src.getAddr().getAddress1(), UserDTO::setAddrAddress1);
        });

        System.out.println(typeMap.map(user));

    }

}

結果:

UserDTO(userId=user001, userName=テスト, age=18, gender=male, addrZipCode=984-0031, addrAddress1=○○県, addrAddress2=null, addrAddress3=null)

SpringBootのControllerにてFormのBeanから自分のDTOに変換するに使うと便利です。

MapStruct

使い方

build.gradleに追加

build.gradle
plugins {
    id 'java'
    id 'net.ltgt.apt' version '0.20'
}

repositories {
    jcenter()
}

dependencies {
    // lombok
    compileOnly "org.projectlombok:lombok:1.18.12"
    annotationProcessor "org.projectlombok:lombok:1.18.12"

    // https://mvnrepository.com/artifact/org.mapstruct/mapstruct
    implementation  group: 'org.mapstruct', name: 'mapstruct', version: '1.3.1.Final'

    // https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor
    annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.3.1.Final'

}

[compileJava, compileTestJava]*.options*.encoding= "UTF-8"

sourceSets {
    main.java.srcDirs += "build/generated/sources/annotationProcessor/java/main"
}

Mapperインタフェース

package com.test.lombok;

import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserMapper {
    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mapping(source = "addr.zipCode", target = "addrZipCode")
    @Mapping(source = "addr.address1", target = "addrAddress1")
    @Mapping(source = "addr.address2", target = "addrAddress2")
    UserDTO userToUserDTO(User user);
}

上記定義し、ビルドするとbuild\generated\sources\annotationProcessor\java\main\com\test\lombokにUserMapperImpl.javaクラスが生成されます。

UserMapperImpl.java
package com.test.lombok;

import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-06-28T21:47:47+0900",
    comments = "version: 1.3.1.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)"
)
public class UserMapperImpl implements UserMapper {

    @Override
    public UserDTO userToUserDTO(User user) {
        if ( user == null ) {
            return null;
        }

        UserDTO userDTO = new UserDTO();

        userDTO.setAddrAddress2( userAddrAddress2( user ) );
        userDTO.setAddrAddress1( userAddrAddress1( user ) );
        userDTO.setAddrZipCode( userAddrZipCode( user ) );
        userDTO.setUserId( user.getUserId() );
        userDTO.setUserName( user.getUserName() );
        userDTO.setAge( user.getAge() );
        userDTO.setGender( user.getGender() );

        return userDTO;
    }

    private String userAddrAddress2(User user) {
        if ( user == null ) {
            return null;
        }
        Address addr = user.getAddr();
        if ( addr == null ) {
            return null;
        }
        String address2 = addr.getAddress2();
        if ( address2 == null ) {
            return null;
        }
        return address2;
    }

    private String userAddrAddress1(User user) {
        if ( user == null ) {
            return null;
        }
        Address addr = user.getAddr();
        if ( addr == null ) {
            return null;
        }
        String address1 = addr.getAddress1();
        if ( address1 == null ) {
            return null;
        }
        return address1;
    }

    private String userAddrZipCode(User user) {
        if ( user == null ) {
            return null;
        }
        Address addr = user.getAddr();
        if ( addr == null ) {
            return null;
        }
        String zipCode = addr.getZipCode();
        if ( zipCode == null ) {
            return null;
        }
        return zipCode;
    }
}

テストコード

package com.test.lombok;

public class TestMain {

    public static void main(String[] args) throws Exception {

        User user = new User();
        user.setUserId("user001");
        user.setUserName("テスト");
        user.setAge("18");
        user.setGender("male");
        Address addr = new Address();
        addr.setZipCode("984-0031");
        addr.setAddress1("○○県");
        user.setAddr(addr);


        UserDTO userDTO = UserMapper.INSTANCE.userToUserDTO(user);

        System.out.println(userDTO);

    }
}

結果:

UserDTO(userId=user001, userName=テスト, age=18, gender=male, addrZipCode=984-0031, addrAddress1=○○県, addrAddress2=null, addrAddress3=null)

ClassNotFoundエラー

Exception in thread "main" java.lang.ExceptionInInitializerError
    at com.test.lombok.TestMain.main(TestMain.java:18)
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot find implementation for com.test.lombok.UserMapper
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:61)
    at com.test.lombok.UserMapper.<clinit>(UserMapper.java:9)
    ... 1 more
Caused by: java.lang.ClassNotFoundException: Cannot find implementation for com.test.lombok.UserMapper
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:75)
    at org.mapstruct.factory.Mappers.getMapper(Mappers.java:58)

build.gradleにsourceSetsを追加

build.gradle
sourceSets {
    main.java.srcDirs += "build/generated/sources/annotationProcessor/java/main"
}

詳細Doc:https://mapstruct.org/documentation/stable/reference/html/

Dozer

使い方

build.gradleに追加

build.gradle
// https://mvnrepository.com/artifact/com.github.dozermapper/dozer-core
compile group: 'com.github.dozermapper', name: 'dozer-core', version: '6.5.0'

テストコード

package com.test.lombok;

import org.modelmapper.ModelMapper;
import org.modelmapper.TypeMap;

import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;

public class TestMain {

    public static void main(String[] args) throws Exception {

        User user = new User();
        user.setUserId("user001");
        user.setUserName("テスト");
        user.setAge("18");
        user.setGender("male");
        Address addr = new Address();
        addr.setZipCode("984-0031");
        addr.setAddress1("○○県");
        user.setAddr(addr);

        Mapper mapper = DozerBeanMapperBuilder.buildDefault();
        UserDTO userDTO = mapper.map(user, UserDTO.class);

        System.out.println(userDTO);

    }
}

結果:

UserDTO(userId=user001, userName=テスト, age=18, gender=male, addrZipCode=null, addrAddress1=null, addrAddress2=null, addrAddress3=null)

同じ項目名はコピーされましたが、それ以外nullでした。

違う項目の場合はXML、API、アノテーションで定義して対応可能

違う項目名の場合は @Mapping("binaryData")のように設定で対応可能ですが、複雑のタイプの場合はXMLで設定する必要があります。

好きなライブラリを選択すればと思います。

以上

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7