1. 程式人生 > 其它 >MapStruct文件(十二)——protobuf對映

MapStruct文件(十二)——protobuf對映

技術標籤:mapstructjavajavabean

目錄

11.1、protobuf2

11.2、protobuf3


11.1、protobuf2

定義一些基礎類


enum DownloadResourceTypeEnum{
  download_resource_type_enum_audio = 0; //音訊
  download_resource_type_enum_vedio = 1;  //視訊
  download_resource_type_enum_picture = 2; //圖片
  download_resource_type_enum_txtfile = 3; //txt檔案
  download_resource_type_enum_wordfile = 4; //word文件
  download_resource_type_enum_excelfile = 5;  //excel文件
  download_resource_type_enum_pptfile = 6;    //ppt文件
  download_resource_type_enum_pdffile = 7;    //pdf文件
}

message Test {
  optional int32 id = 1;
  optional string name = 2;
  optional string create_time = 3;
  repeated Item item = 4;
  optional DownloadResourceTypeEnum download_resource_type_enum = 5;
  optional Item main_item = 6;
  optional bool disable = 7;
  optional float pre_price = 8;
  map<string, string> kv = 9;
  oneof test_oneof {
    string one_string = 10;
    int32 one_int = 11;
  }
  repeated string number = 12;
  repeated DownloadResourceTypeEnum types = 13;
  repeated int32 nos = 14;
}

message Item {
  optional int64 item_id = 1;
  optional double price = 2;
  optional uint32 uint32_count = 3; // 對應java int
  optional uint64 uint64_count = 4; // 對應java Long
  optional sint32 sint32_count = 5; // 對應java INT 比常規int32更有效地編碼負數
  optional sint64 sint64_count = 6; // 對應java long 比常規int64更有效地編碼負數
  optional fixed32 fixed32_count = 7; // 對應java int 總是四個位元組。如果值通常大於228,則比uint32更有效
  optional fixed64 fixed64_count = 8; // 對應java Long  總是八個位元組。如果值通常大於256,則比uint64更有效
  optional sfixed32 sfixed32_count = 9; // 對應java INT 總是四個位元組。
  optional sfixed64 sfixed64_count = 10; // 對應java long 總是八個位元組。
  optional bytes type = 11; // 不可變的位元組陣列 不涉及轉碼 通常用於傳輸字元流
}
 
@Data
public class TestDTO {

    private Integer id;

    private String name;

    private Date createTime;

    private TypeEnum downloadResourceTypeEnum;

    private ItemDTO mainItem;

    private Boolean disable;

    private BigDecimal prePrice;

    private Map<String, String> kv;

    private String oneString;

    private Integer oneInt;

    private List<ItemDTO> itemList;

    private List<String> numberList;

    private List<TypeEnum> typesList;

    private List<Integer> nosList;
}
 
@Data
public class ItemDTO {

    private Long itemId;

    private Double price;

    private Integer uint32Count;

    private Long uint64Count;

    private Integer sint32Count;

    private Long sint64Count;

    private Integer fixed32Count;

    private Long fixed64Count;

    private Integer sfixed32Count;

    private Long sfixed64Count;

    private byte[] type;
}

使用


@Mapper
public class BaseMapper {

    @ObjectFactory
    public ProtocolStringList createProtocolStringList(List<String> list) {
        return new LazyStringArrayList(list.size());
    }

    public static byte[] toByte(ByteString bytes) {
        return bytes.toByteArray();
    }

}
 
@Mapper(uses = {ByteString.class, BaseMapper.class}, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
 collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface TestMapper {

    Test toProto(TestDTO testDTO);

    TestDTO toDTO(Test test);

}
 

編譯的結果


@Component
public class TestMapperImpl implements TestMapper {

    @Autowired
    private ByteString byteString;

    @Override
    public Test toProto(TestDTO testDTO) {
        if ( testDTO == null ) {
            return null;
        }

        Builder test = Test.newBuilder();

        if ( testDTO.getId() != null ) {
            test.setId( testDTO.getId() );
        }
        try {
            if ( testDTO.getName() != null ) {
                test.setName( byteString.toString( testDTO.getName() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( testDTO.getCreateTime() != null ) {
            test.setCreateTime( new SimpleDateFormat().format( testDTO.getCreateTime() ) );
        }
        if ( testDTO.getDownloadResourceTypeEnum() != null ) {
            test.setDownloadResourceTypeEnum( typeEnumToDownloadResourceEnum( testDTO.getDownloadResourceTypeEnum() ) );
        }
        if ( testDTO.getMainItem() != null ) {
            test.setMainItem( itemDTOToItem( testDTO.getMainItem() ) );
        }
        if ( testDTO.getDisable() != null ) {
            test.setDisable( testDTO.getDisable() );
        }
        if ( testDTO.getPrePrice() != null ) {
            test.setPrePrice( testDTO.getPrePrice().floatValue() );
        }
        try {
            if ( testDTO.getOneString() != null ) {
                test.setOneString( byteString.toString( testDTO.getOneString() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( testDTO.getOneInt() != null ) {
            test.setOneInt( testDTO.getOneInt() );
        }
        if ( testDTO.getItemList() != null ) {
            for ( ItemDTO itemList : testDTO.getItemList() ) {
                test.addItem( itemDTOToItem( itemList ) );
            }
        }
        if ( test.getKv() != null ) {
            Map<String, String> map = testDTO.getKv();
            if ( map != null ) {
                test.getKv().putAll( map );
            }
        }
        try {
            if ( testDTO.getNumberList() != null ) {
                for ( String numberList : testDTO.getNumberList() ) {
                    test.addNumber( byteString.toString( numberList ) );
                }
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( testDTO.getTypesList() != null ) {
            for ( TypeEnum typesList : testDTO.getTypesList() ) {
                test.addTypes( typeEnumToDownloadResourceEnum( typesList ) );
            }
        }
        if ( test.getNosList() != null ) {
            List<Integer> list = testDTO.getNosList();
            if ( list != null ) {
                test.getNosList().addAll( list );
            }
        }

        return test.build();
    }

    @Override
    public TestDTO toDTO(Test test) {
        if ( test == null ) {
            return null;
        }

        TestDTO testDTO = new TestDTO();

        if ( test.hasId() ) {
            testDTO.setId( test.getId() );
        }
        try {
            if ( test.hasName() ) {
                testDTO.setName( byteString.toString( test.getName() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        try {
            if ( test.hasCreateTime() ) {
                testDTO.setCreateTime( new SimpleDateFormat().parse( test.getCreateTime() ) );
            }
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
        if ( test.hasDownloadResourceTypeEnum() ) {
            testDTO.setDownloadResourceTypeEnum( downloadResourceEnumToTypeEnum( test.getDownloadResourceTypeEnum() ) );
        }
        if ( test.hasMainItem() ) {
            testDTO.setMainItem( itemToItemDTO( test.getMainItem() ) );
        }
        if ( test.hasDisable() ) {
            testDTO.setDisable( test.getDisable() );
        }
        if ( test.hasPrePrice() ) {
            testDTO.setPrePrice( BigDecimal.valueOf( test.getPrePrice() ) );
        }
        Map<String, String> map = test.getKv();
        if ( map != null ) {
            testDTO.setKv( new HashMap<String, String>( map ) );
        }
        try {
            if ( test.hasOneString() ) {
                testDTO.setOneString( byteString.toString( test.getOneString() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( test.hasOneInt() ) {
            testDTO.setOneInt( test.getOneInt() );
        }
        List<ItemDTO> list = itemListToItemDTOList( test.getItemList() );
        if ( list != null ) {
            testDTO.setItemList( list );
        }
        ProtocolStringList protocolStringList = test.getNumberList();
        if ( protocolStringList != null ) {
            testDTO.setNumberList( new ArrayList<String>( protocolStringList ) );
        }
        List<TypeEnum> list1 = downloadResourceEnumListToTypeEnumList( test.getTypesList() );
        if ( list1 != null ) {
            testDTO.setTypesList( list1 );
        }
        List<Integer> list2 = test.getNosList();
        if ( list2 != null ) {
            testDTO.setNosList( new ArrayList<Integer>( list2 ) );
        }

        return testDTO;
    }

    protected DownloadResourceEnum typeEnumToDownloadResourceEnum(TypeEnum typeEnum) {
        if ( typeEnum == null ) {
            return null;
        }

        DownloadResourceEnum downloadResourceEnum;

        switch ( typeEnum ) {
            case download_resource_type_enum_audio: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_audio;
            break;
            case download_resource_type_enum_vedio: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_vedio;
            break;
            case download_resource_type_enum_picture: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_picture;
            break;
            case download_resource_type_enum_txtfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_txtfile;
            break;
            case download_resource_type_enum_wordfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_wordfile;
            break;
            case download_resource_type_enum_excelfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_excelfile;
            break;
            case download_resource_type_enum_pptfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_pptfile;
            break;
            case download_resource_type_enum_pdffile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_pdffile;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + typeEnum );
        }

        return downloadResourceEnum;
    }

    protected Item itemDTOToItem(ItemDTO itemDTO) {
        if ( itemDTO == null ) {
            return null;
        }

        com.ljc.orika.proto.Item.Builder item = Item.newBuilder();

        if ( itemDTO.getItemId() != null ) {
            item.setItemId( itemDTO.getItemId() );
        }
        if ( itemDTO.getPrice() != null ) {
            item.setPrice( itemDTO.getPrice() );
        }
        if ( itemDTO.getUint32Count() != null ) {
            item.setUint32Count( itemDTO.getUint32Count() );
        }
        if ( itemDTO.getUint64Count() != null ) {
            item.setUint64Count( itemDTO.getUint64Count() );
        }
        if ( itemDTO.getSint32Count() != null ) {
            item.setSint32Count( itemDTO.getSint32Count() );
        }
        if ( itemDTO.getSint64Count() != null ) {
            item.setSint64Count( itemDTO.getSint64Count() );
        }
        if ( itemDTO.getFixed32Count() != null ) {
            item.setFixed32Count( itemDTO.getFixed32Count() );
        }
        if ( itemDTO.getFixed64Count() != null ) {
            item.setFixed64Count( itemDTO.getFixed64Count() );
        }
        if ( itemDTO.getSfixed32Count() != null ) {
            item.setSfixed32Count( itemDTO.getSfixed32Count() );
        }
        if ( itemDTO.getSfixed64Count() != null ) {
            item.setSfixed64Count( itemDTO.getSfixed64Count() );
        }
        if ( itemDTO.getType() != null ) {
            item.setType( ByteString.copyFrom( itemDTO.getType() ) );
        }

        return item.build();
    }

    protected TypeEnum downloadResourceEnumToTypeEnum(DownloadResourceEnum downloadResourceEnum) {
        if ( downloadResourceEnum == null ) {
            return null;
        }

        TypeEnum typeEnum;

        switch ( downloadResourceEnum ) {
            case download_resource_type_enum_audio: typeEnum = TypeEnum.download_resource_type_enum_audio;
            break;
            case download_resource_type_enum_vedio: typeEnum = TypeEnum.download_resource_type_enum_vedio;
            break;
            case download_resource_type_enum_picture: typeEnum = TypeEnum.download_resource_type_enum_picture;
            break;
            case download_resource_type_enum_txtfile: typeEnum = TypeEnum.download_resource_type_enum_txtfile;
            break;
            case download_resource_type_enum_wordfile: typeEnum = TypeEnum.download_resource_type_enum_wordfile;
            break;
            case download_resource_type_enum_excelfile: typeEnum = TypeEnum.download_resource_type_enum_excelfile;
            break;
            case download_resource_type_enum_pptfile: typeEnum = TypeEnum.download_resource_type_enum_pptfile;
            break;
            case download_resource_type_enum_pdffile: typeEnum = TypeEnum.download_resource_type_enum_pdffile;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + downloadResourceEnum );
        }

        return typeEnum;
    }

    protected ItemDTO itemToItemDTO(Item item) {
        if ( item == null ) {
            return null;
        }

        ItemDTO itemDTO = new ItemDTO();

        if ( item.hasItemId() ) {
            itemDTO.setItemId( item.getItemId() );
        }
        if ( item.hasPrice() ) {
            itemDTO.setPrice( item.getPrice() );
        }
        if ( item.hasUint32Count() ) {
            itemDTO.setUint32Count( item.getUint32Count() );
        }
        if ( item.hasUint64Count() ) {
            itemDTO.setUint64Count( item.getUint64Count() );
        }
        if ( item.hasSint32Count() ) {
            itemDTO.setSint32Count( item.getSint32Count() );
        }
        if ( item.hasSint64Count() ) {
            itemDTO.setSint64Count( item.getSint64Count() );
        }
        if ( item.hasFixed32Count() ) {
            itemDTO.setFixed32Count( item.getFixed32Count() );
        }
        if ( item.hasFixed64Count() ) {
            itemDTO.setFixed64Count( item.getFixed64Count() );
        }
        if ( item.hasSfixed32Count() ) {
            itemDTO.setSfixed32Count( item.getSfixed32Count() );
        }
        if ( item.hasSfixed64Count() ) {
            itemDTO.setSfixed64Count( item.getSfixed64Count() );
        }
        if ( item.hasType() ) {
            itemDTO.setType( BaseMapper.toByte( item.getType() ) );
        }

        return itemDTO;
    }

    protected List<ItemDTO> itemListToItemDTOList(List<Item> list) {
        if ( list == null ) {
            return null;
        }

        List<ItemDTO> list1 = new ArrayList<ItemDTO>( list.size() );
        for ( Item item : list ) {
            list1.add( itemToItemDTO( item ) );
        }

        return list1;
    }

    protected List<TypeEnum> downloadResourceEnumListToTypeEnumList(List<DownloadResourceEnum> list) {
        if ( list == null ) {
            return null;
        }

        List<TypeEnum> list1 = new ArrayList<TypeEnum>( list.size() );
        for ( DownloadResourceEnum downloadResourceEnum : list ) {
            list1.add( downloadResourceEnumToTypeEnum( downloadResourceEnum ) );
        }

        return list1;
    }
}

proto->bean:

看一下protoc生成的java類中的repeated型別的欄位。

get集合的方法後面加上了“List”,就如第4.5章所介紹的,所以強烈建議javabean對應的List<T>型別的欄位名同名且後面加上“List”

因為optional型別的欄位不能設為null,也推薦nullValueCheckStrategy設為NullValueCheckStrategy.ALWAYS;還可以發現生成的proto對映bean的方法中,會自動呼叫hasXXX方法判斷"null"。

若有bytes型別的欄位,要轉成byte[],要匯入ByteString.class

當使用的是預設的集合對映策略,若有要轉成List<String>,要手動建立一個工廠方法,正如BaseMapper#createProtocolStringList,因為自動對映生成的方法時會newProtocolStringList(),然而ProtocolStringList是抽象類。

getNumberList返回的型別是ProtocolStringList

若使用的是CollectionMappingStrategy.ADDER_PREFERRED,因為會一個個add進去,所以無需再呼叫工廠方法;但又因為可能匹配到多個add方法,所以最終還是會使用get-addAll的策略。

bean->proto:

注意byte[]bytes時,要手動指定一個對映方法,正如BaseMapper#toByte方法。

不能更新protobuf物件,更新總是會clear-get-addAll:

使用protobuf的builder物件操作時,其中一個原因List<Long>型別的實現是一個UnmodifiableRandomAccessList,mapstruct在更新list時會clear集合,使用會拋異常。

使用protobuf物件而非builder時,集合是一個EmptyList,增刪改查也會拋異常。

使用預設的集合對映策略的結果


@Component
public class TestMapperImpl implements TestMapper {

    @Autowired
    private ByteString byteString;
    @Autowired
    private BaseMapper baseMapper;

    @Override
    public Test toProto(TestDTO testDTO) {
        if ( testDTO == null ) {
            return null;
        }

        Builder test = Test.newBuilder();

        if ( testDTO.getId() != null ) {
            test.setId( testDTO.getId() );
        }
        try {
            if ( testDTO.getName() != null ) {
                test.setName( byteString.toString( testDTO.getName() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( testDTO.getCreateTime() != null ) {
            test.setCreateTime( new SimpleDateFormat().format( testDTO.getCreateTime() ) );
        }
        if ( testDTO.getDownloadResourceTypeEnum() != null ) {
            test.setDownloadResourceTypeEnum( typeEnumToDownloadResourceEnum( testDTO.getDownloadResourceTypeEnum() ) );
        }
        if ( testDTO.getMainItem() != null ) {
            test.setMainItem( itemDTOToItem( testDTO.getMainItem() ) );
        }
        if ( testDTO.getDisable() != null ) {
            test.setDisable( testDTO.getDisable() );
        }
        if ( testDTO.getPrePrice() != null ) {
            test.setPrePrice( testDTO.getPrePrice().floatValue() );
        }
        try {
            if ( testDTO.getOneString() != null ) {
                test.setOneString( byteString.toString( testDTO.getOneString() ) );
            }
        }
        catch ( UnsupportedEncodingException e ) {
            throw new RuntimeException( e );
        }
        if ( testDTO.getOneInt() != null ) {
            test.setOneInt( testDTO.getOneInt() );
        }
        if ( test.getItemList() != null ) {
            List<Item> list = itemDTOListToItemList( testDTO.getItemList() );
            if ( list != null ) {
                test.getItemList().addAll( list );
            }
        }
        if ( test.getKv() != null ) {
            Map<String, String> map = testDTO.getKv();
            if ( map != null ) {
                test.getKv().putAll( map );
            }
        }
        if ( test.getNumberList() != null ) {
            try {
                ProtocolStringList protocolStringList = stringListToProtocolStringList( testDTO.getNumberList() );
                if ( protocolStringList != null ) {
                    test.getNumberList().addAll( protocolStringList );
                }
            }
            catch ( UnsupportedEncodingException e ) {
                throw new RuntimeException( e );
            }
        }
        if ( test.getTypesList() != null ) {
            List<DownloadResourceEnum> list1 = typeEnumListToDownloadResourceEnumList( testDTO.getTypesList() );
            if ( list1 != null ) {
                test.getTypesList().addAll( list1 );
            }
        }
        if ( test.getNosList() != null ) {
            List<Integer> list2 = testDTO.getNosList();
            if ( list2 != null ) {
                test.getNosList().addAll( list2 );
            }
        }

        return test.build();
    }

    protected DownloadResourceEnum typeEnumToDownloadResourceEnum(TypeEnum typeEnum) {
        if ( typeEnum == null ) {
            return null;
        }

        DownloadResourceEnum downloadResourceEnum;

        switch ( typeEnum ) {
            case download_resource_type_enum_audio: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_audio;
            break;
            case download_resource_type_enum_vedio: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_vedio;
            break;
            case download_resource_type_enum_picture: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_picture;
            break;
            case download_resource_type_enum_txtfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_txtfile;
            break;
            case download_resource_type_enum_wordfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_wordfile;
            break;
            case download_resource_type_enum_excelfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_excelfile;
            break;
            case download_resource_type_enum_pptfile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_pptfile;
            break;
            case download_resource_type_enum_pdffile: downloadResourceEnum = DownloadResourceEnum.download_resource_type_enum_pdffile;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + typeEnum );
        }

        return downloadResourceEnum;
    }

    protected Item itemDTOToItem(ItemDTO itemDTO) {
        if ( itemDTO == null ) {
            return null;
        }

        com.ljc.orika.proto.Item.Builder item = Item.newBuilder();

        if ( itemDTO.getItemId() != null ) {
            item.setItemId( itemDTO.getItemId() );
        }
        if ( itemDTO.getPrice() != null ) {
            item.setPrice( itemDTO.getPrice() );
        }
        if ( itemDTO.getUint32Count() != null ) {
            item.setUint32Count( itemDTO.getUint32Count() );
        }
        if ( itemDTO.getUint64Count() != null ) {
            item.setUint64Count( itemDTO.getUint64Count() );
        }
        if ( itemDTO.getSint32Count() != null ) {
            item.setSint32Count( itemDTO.getSint32Count() );
        }
        if ( itemDTO.getSint64Count() != null ) {
            item.setSint64Count( itemDTO.getSint64Count() );
        }
        if ( itemDTO.getFixed32Count() != null ) {
            item.setFixed32Count( itemDTO.getFixed32Count() );
        }
        if ( itemDTO.getFixed64Count() != null ) {
            item.setFixed64Count( itemDTO.getFixed64Count() );
        }
        if ( itemDTO.getSfixed32Count() != null ) {
            item.setSfixed32Count( itemDTO.getSfixed32Count() );
        }
        if ( itemDTO.getSfixed64Count() != null ) {
            item.setSfixed64Count( itemDTO.getSfixed64Count() );
        }
        if ( itemDTO.getType() != null ) {
            item.setType( ByteString.copyFrom( itemDTO.getType() ) );
        }

        return item.build();
    }

    protected List<Item> itemDTOListToItemList(List<ItemDTO> list) {
        if ( list == null ) {
            return null;
        }

        List<Item> list1 = new ArrayList<Item>( list.size() );
        for ( ItemDTO itemDTO : list ) {
            list1.add( itemDTOToItem( itemDTO ) );
        }

        return list1;
    }

    protected ProtocolStringList stringListToProtocolStringList(List<String> list) throws UnsupportedEncodingException {
        if ( list == null ) {
            return null;
        }

        ProtocolStringList protocolStringList = baseMapper.createProtocolStringList( list );
        for ( String string : list ) {
            protocolStringList.add( byteString.toString( string ) );
        }

        return protocolStringList;
    }

    protected List<DownloadResourceEnum> typeEnumListToDownloadResourceEnumList(List<TypeEnum> list) {
        if ( list == null ) {
            return null;
        }

        List<DownloadResourceEnum> list1 = new ArrayList<DownloadResourceEnum>( list.size() );
        for ( TypeEnum typeEnum : list ) {
            list1.add( typeEnumToDownloadResourceEnum( typeEnum ) );
        }

        return list1;
    }
}

11.2、protobuf3

protobuf3和protobuf2基本相同,區別在於生成的列舉物件會多一個UNRECOGNIZED。

所以在對映設,注意設此列舉對映為null。

@ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL)
TypeEnum toTypeEnum(DownloadResourceTypeEnum downloadResourceTypeEnum);

帶有Any型別的對映


@EqualsAndHashCode(callSuper = true)
@Data
public class Test3DTO extends TestDTO {


    private ItemDTO details;
}
 
message Test3 {
  int32 id = 1;
  string name = 2;
  string create_time = 3;
  repeated Item3 item = 4;
  DownloadResourceTypeEnum download_resource_type_enum = 5;
  Item3 main_item = 6;
  bool disable = 7;
  float pre_price = 8;
  map<string, string> kv = 9;
  oneof test_oneof {
    string one_string = 10;
    int32 one_int = 11;
  }
  repeated string number = 12;
  repeated DownloadResourceTypeEnum types = 13;
  repeated int32 nos = 14;
  google.protobuf.Any details = 15;
}

message Item3 {
 int64 item_id = 1;
 double price = 2;
 uint32 uint32_count = 3; // 對應java int
 uint64 uint64_count = 4; // 對應java Long
 sint32 sint32_count = 5; // 對應java INT 比常規int32更有效地編碼負數
 sint64 sint64_count = 6; // 對應java long 比常規int64更有效地編碼負數
 fixed32 fixed32_count = 7; // 對應java int 總是四個位元組。如果值通常大於228,則比uint32更有效
 fixed64 fixed64_count = 8; // 對應java Long  總是八個位元組。如果值通常大於256,則比uint64更有效
 sfixed32 sfixed32_count = 9; // 對應java INT 總是四個位元組。
 sfixed64 sfixed64_count = 10; // 對應java long 總是八個位元組。
 bytes type = 11; // 不可變的位元組陣列 不涉及轉碼 通常用於傳輸字元流
}
 
enum DownloadResourceTypeEnum{
  download_resource_type_enum_audio = 0; //音訊
  download_resource_type_enum_vedio = 1;  //視訊
  download_resource_type_enum_picture = 2; //圖片
  download_resource_type_enum_txtfile = 3; //txt檔案
  download_resource_type_enum_wordfile = 4; //word文件
  download_resource_type_enum_excelfile = 5;  //excel文件
  download_resource_type_enum_pptfile = 6;    //ppt文件
  download_resource_type_enum_pdffile = 7;    //pdf文件
}


@Mapper
public class BaseMapper {

    @ObjectFactory
    public ProtocolStringList createProtocolStringList(List<String> list) {
        return new LazyStringArrayList(list.size());
    }

    public static byte[] toByte(ByteString bytes) {
        return bytes.toByteArray();
    }

    public static ByteString copyFrom(byte[] bytes) {
        return ByteString.copyFrom(bytes);
    }

    // any轉javabean的中間轉換方法
    public static <T extends GeneratedMessageV3> T unpack(Any any, @TargetType Class<T> clazz) {
        T unpack;
        try {
            unpack  = any.unpack(clazz);
        } catch (InvalidProtocolBufferException e) {
            return null;
        }
        return unpack;
    }

	// 這裡只是為了在mapstruct編譯找最合適方法時中間的過渡,並不是直接呼叫,但一定要加,具體分析參見第12.6章
	public static <T extends GeneratedMessageV3> Any pack(T message) {
    	return Any.pack(message);
	}
	
    // protobuf轉javabean的中間轉換方法
    public static Any packGeneratedMessageV3(GeneratedMessageV3 message) {
        return Any.pack(message);
    }
 
	public static Date toDateFromString(String dateString, String format) {
    	if (StringUtils.isEmpty(dateString)) {
        	return null;
    	}
    	Date date;
    	try {
        	date = new SimpleDateFormat(format).parse(dateString);
    	} catch (ParseException e) {
        	return null;
    	}
    	return date;
	}

}
 
@Mapper(uses = { BaseMapper.class}, nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS,
        collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
public interface TestMapper {

    Test3 toProto(Test3DTO test3DTO);

	// 物件屬性的轉換一定要手動定義方法
    Item3 toProtoItem(ItemDTO itemDTO);

	@Mapping(target = "createTime", expression = "java(BaseMapper.toDateFromString(test.getCreateTime(), \"yyyy-mm-dd\"))")
    Test3DTO toDTO(Test3 test);

	// 物件屬性的轉換一定要手動定義方法
    ItemDTO toItemDTO(Item3 item3);

    @ValueMapping(source = "UNRECOGNIZED", target = MappingConstants.NULL)
    TypeEnum toTypeEnum(DownloadResourceTypeEnum downloadResourceTypeEnum);

}
 
@Component
public class TestMapperImpl implements TestMapper {

    @Override
    public Test3 toProto(Test3DTO test3DTO) {
        if ( test3DTO == null ) {
            return null;
        }

        Builder test3 = Test3.newBuilder();

        if ( test3DTO.getId() != null ) {
            test3.setId( test3DTO.getId() );
        }
        if ( test3DTO.getName() != null ) {
            test3.setName( test3DTO.getName() );
        }
        if ( test3DTO.getCreateTime() != null ) {
            test3.setCreateTime( new SimpleDateFormat().format( test3DTO.getCreateTime() ) );
        }
        if ( test3DTO.getDownloadResourceTypeEnum() != null ) {
            test3.setDownloadResourceTypeEnum( typeEnumToDownloadResourceTypeEnum( test3DTO.getDownloadResourceTypeEnum() ) );
        }
        if ( test3DTO.getMainItem() != null ) {
            test3.setMainItem( toProtoItem( test3DTO.getMainItem() ) );
        }
        if ( test3DTO.getDisable() != null ) {
            test3.setDisable( test3DTO.getDisable() );
        }
        if ( test3DTO.getPrePrice() != null ) {
            test3.setPrePrice( test3DTO.getPrePrice().floatValue() );
        }
        if ( test3DTO.getOneString() != null ) {
            test3.setOneString( test3DTO.getOneString() );
        }
        if ( test3DTO.getOneInt() != null ) {
            test3.setOneInt( test3DTO.getOneInt() );
        }
        if ( test3DTO.getDetails() != null ) {
            test3.setDetails( BaseMapper.packGeneratedMessageV3( toProtoItem( test3DTO.getDetails() ) ) );
        }
        if ( test3DTO.getItemList() != null ) {
            for ( ItemDTO itemList : test3DTO.getItemList() ) {
                test3.addItem( toProtoItem( itemList ) );
            }
        }
        if ( test3.getKv() != null ) {
            Map<String, String> map = test3DTO.getKv();
            if ( map != null ) {
                test3.getKv().putAll( map );
            }
        }
        if ( test3DTO.getNumberList() != null ) {
            for ( String numberList : test3DTO.getNumberList() ) {
                test3.addNumber( numberList );
            }
        }
        if ( test3DTO.getTypesList() != null ) {
            for ( TypeEnum typesList : test3DTO.getTypesList() ) {
                test3.addTypes( typeEnumToDownloadResourceTypeEnum( typesList ) );
            }
        }
        if ( test3.getNosList() != null ) {
            List<Integer> list = test3DTO.getNosList();
            if ( list != null ) {
                test3.getNosList().addAll( list );
            }
        }

        return test3.build();
    }

    @Override
    public Item3 toProtoItem(ItemDTO itemDTO) {
        if ( itemDTO == null ) {
            return null;
        }

        com.ljc.orika.proto3.Item3.Builder item3 = Item3.newBuilder();

        if ( itemDTO.getItemId() != null ) {
            item3.setItemId( itemDTO.getItemId() );
        }
        if ( itemDTO.getPrice() != null ) {
            item3.setPrice( itemDTO.getPrice() );
        }
        if ( itemDTO.getUint32Count() != null ) {
            item3.setUint32Count( itemDTO.getUint32Count() );
        }
        if ( itemDTO.getUint64Count() != null ) {
            item3.setUint64Count( itemDTO.getUint64Count() );
        }
        if ( itemDTO.getSint32Count() != null ) {
            item3.setSint32Count( itemDTO.getSint32Count() );
        }
        if ( itemDTO.getSint64Count() != null ) {
            item3.setSint64Count( itemDTO.getSint64Count() );
        }
        if ( itemDTO.getFixed32Count() != null ) {
            item3.setFixed32Count( itemDTO.getFixed32Count() );
        }
        if ( itemDTO.getFixed64Count() != null ) {
            item3.setFixed64Count( itemDTO.getFixed64Count() );
        }
        if ( itemDTO.getSfixed32Count() != null ) {
            item3.setSfixed32Count( itemDTO.getSfixed32Count() );
        }
        if ( itemDTO.getSfixed64Count() != null ) {
            item3.setSfixed64Count( itemDTO.getSfixed64Count() );
        }
        if ( itemDTO.getType() != null ) {
            item3.setType( BaseMapper.copyFrom( itemDTO.getType() ) );
        }

        return item3.build();
    }

    @Override
    public Test3DTO toDTO(Test3 test) {
        if ( test == null ) {
            return null;
        }

        Test3DTO test3DTO = new Test3DTO();

        test3DTO.setId( test.getId() );
        if ( test.getName() != null ) {
            test3DTO.setName( test.getName() );
        }
        testDTO.setCreateTime( BaseMapper.toDateFromString(test.getCreateTime(), "yyyy-mm-dd") );
        if ( test.getDownloadResourceTypeEnum() != null ) {
            test3DTO.setDownloadResourceTypeEnum( toTypeEnum( test.getDownloadResourceTypeEnum() ) );
        }
        if ( test.hasMainItem() ) {
            test3DTO.setMainItem( toItemDTO( test.getMainItem() ) );
        }
        test3DTO.setDisable( test.getDisable() );
        test3DTO.setPrePrice( BigDecimal.valueOf( test.getPrePrice() ) );
        Map<String, String> map = test.getKv();
        if ( map != null ) {
            test3DTO.setKv( new HashMap<String, String>( map ) );
        }
        if ( test.getOneString() != null ) {
            test3DTO.setOneString( test.getOneString() );
        }
        test3DTO.setOneInt( test.getOneInt() );
        List<ItemDTO> list = item3ListToItemDTOList( test.getItemList() );
        if ( list != null ) {
            test3DTO.setItemList( list );
        }
        ProtocolStringList protocolStringList = test.getNumberList();
        if ( protocolStringList != null ) {
            test3DTO.setNumberList( new ArrayList<String>( protocolStringList ) );
        }
        List<TypeEnum> list1 = downloadResourceTypeEnumListToTypeEnumList( test.getTypesList() );
        if ( list1 != null ) {
            test3DTO.setTypesList( list1 );
        }
        List<Integer> list2 = test.getNosList();
        if ( list2 != null ) {
            test3DTO.setNosList( new ArrayList<Integer>( list2 ) );
        }
        if ( test.hasDetails() ) {
            test3DTO.setDetails( toItemDTO( BaseMapper.unpack( test.getDetails(), Item3.class ) ) );
        }

        return test3DTO;
    }

    @Override
    public ItemDTO toItemDTO(Item3 item3) {
        if ( item3 == null ) {
            return null;
        }

        ItemDTO itemDTO = new ItemDTO();

        itemDTO.setItemId( item3.getItemId() );
        itemDTO.setPrice( item3.getPrice() );
        itemDTO.setUint32Count( item3.getUint32Count() );
        itemDTO.setUint64Count( item3.getUint64Count() );
        itemDTO.setSint32Count( item3.getSint32Count() );
        itemDTO.setSint64Count( item3.getSint64Count() );
        itemDTO.setFixed32Count( item3.getFixed32Count() );
        itemDTO.setFixed64Count( item3.getFixed64Count() );
        itemDTO.setSfixed32Count( item3.getSfixed32Count() );
        itemDTO.setSfixed64Count( item3.getSfixed64Count() );
        if ( item3.getType() != null ) {
            itemDTO.setType( BaseMapper.toByte( item3.getType() ) );
        }

        return itemDTO;
    }

    @Override
    public TypeEnum toTypeEnum(DownloadResourceTypeEnum downloadResourceTypeEnum) {
        if ( downloadResourceTypeEnum == null ) {
            return null;
        }

        TypeEnum typeEnum;

        switch ( downloadResourceTypeEnum ) {
            case UNRECOGNIZED: typeEnum = null;
            break;
            case download_resource_type_enum_audio: typeEnum = TypeEnum.download_resource_type_enum_audio;
            break;
            case download_resource_type_enum_vedio: typeEnum = TypeEnum.download_resource_type_enum_vedio;
            break;
            case download_resource_type_enum_picture: typeEnum = TypeEnum.download_resource_type_enum_picture;
            break;
            case download_resource_type_enum_txtfile: typeEnum = TypeEnum.download_resource_type_enum_txtfile;
            break;
            case download_resource_type_enum_wordfile: typeEnum = TypeEnum.download_resource_type_enum_wordfile;
            break;
            case download_resource_type_enum_excelfile: typeEnum = TypeEnum.download_resource_type_enum_excelfile;
            break;
            case download_resource_type_enum_pptfile: typeEnum = TypeEnum.download_resource_type_enum_pptfile;
            break;
            case download_resource_type_enum_pdffile: typeEnum = TypeEnum.download_resource_type_enum_pdffile;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + downloadResourceTypeEnum );
        }

        return typeEnum;
    }

    protected DownloadResourceTypeEnum typeEnumToDownloadResourceTypeEnum(TypeEnum typeEnum) {
        if ( typeEnum == null ) {
            return null;
        }

        DownloadResourceTypeEnum downloadResourceTypeEnum;

        switch ( typeEnum ) {
            case download_resource_type_enum_audio: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_audio;
            break;
            case download_resource_type_enum_vedio: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_vedio;
            break;
            case download_resource_type_enum_picture: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_picture;
            break;
            case download_resource_type_enum_txtfile: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_txtfile;
            break;
            case download_resource_type_enum_wordfile: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_wordfile;
            break;
            case download_resource_type_enum_excelfile: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_excelfile;
            break;
            case download_resource_type_enum_pptfile: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_pptfile;
            break;
            case download_resource_type_enum_pdffile: downloadResourceTypeEnum = DownloadResourceTypeEnum.download_resource_type_enum_pdffile;
            break;
            default: throw new IllegalArgumentException( "Unexpected enum constant: " + typeEnum );
        }

        return downloadResourceTypeEnum;
    }

    protected List<ItemDTO> item3ListToItemDTOList(List<Item3> list) {
        if ( list == null ) {
            return null;
        }

        List<ItemDTO> list1 = new ArrayList<ItemDTO>( list.size() );
        for ( Item3 item3 : list ) {
            list1.add( toItemDTO( item3 ) );
        }

        return list1;
    }

    protected List<TypeEnum> downloadResourceTypeEnumListToTypeEnumList(List<DownloadResourceTypeEnum> list) {
        if ( list == null ) {
            return null;
        }

        List<TypeEnum> list1 = new ArrayList<TypeEnum>( list.size() );
        for ( DownloadResourceTypeEnum downloadResourceTypeEnum : list ) {
            list1.add( toTypeEnum( downloadResourceTypeEnum ) );
        }

        return list1;
    }
}

test3.setDetails( BaseMapper.packGeneratedMessageV3( toProtoItem( test3DTO.getDetails() ) ) ),這裡涉及了複雜對映,最裡層轉換方法是呼叫了手動定義的方法,因為通過第3.2章可知,自動生成子對映會在複雜轉換規則之後,Any轉javabean中間要先轉成具體的protobuf物件,不先呼叫Any到具體的protobuf物件的轉換,mapstruct會沒有找到相應的轉換髮現,也不會再呼叫BaseMapper#packGeneratedMessageV3,轉而執行規則5,而自動生成一個,但不能滿足具體轉換。

protobuf3中不再有基礎屬性型別的hasXXX檢查,使用string型別預設值是“”,轉換可能會有NPE,比如轉Date,這裡只有手動指定expression轉換testDTO.setCreateTime( BaseMapper.toDateFromString(test.getCreateTime(), "yyyy-mm-dd") );