Martin
Martin

Reputation: 1

Java ModelMapper Flat Model to Hierarchical Model

I am using www.modelmapper.org and I am working on mapping the same "flat" java DTO into several "hierarchical" DTO.
The "flat" DTO has a number of primitive properties.
The "hierarchical" have a number of complex types that hold a number of primitive types. These DTOs are very similar, but not the same.

"Flat" DTO:

class TransactionRequest
{
    String cashierNumber;
    Long amount;
    Integer currency;

    public String getCashierNumber {..
    ...
}

"Hierarchical DTOs:

class PurchaseRequest
{
    POSInfo posInfo;
    Amount amount;        
    ...    

    public PosInfo getPosInfo {..
    public Amount getAmount { ..
    ...
}

class CancelRequest
{
    POSInfo posInfo;
    Amount amount;        
    ...

    public PosInfo getPosInfo {..
    public Amount getAmount { ..
    ...
}

class Amount
{    
    BigDecimal value;
    Integer currency;

    public Integer getCurrency{..
    ...
}

class PosInfo
{    
    String cashierNumber;

    public String getCashierNumber {..
}

TransactionRequest should be mapped into 1) PurchaseRequest and 2) CancelRequest.
One of the problems is, that the amount has to converted from Long (in minor unit) to a BigDecimal (in major unit with decimal digits). I achieved this by writing my own Long to BigDecimal converter. Now I have the problem to define the mappings required in a reusable fashion. What I don't want is to define the mappings for each target type like this:

class PurchaseMap extends PropertyMap<..
protected void configure()
{
using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
...
}

class CancelMap extends PropertyMap<..
protected void configure()
{
using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
...
}

I would like to have to only one time define the mapping of the Amount DTO (and all other sub type mappings such as PosInfo and many more) and then re-use these mappings. I tried several options:

The first thing I tried was declaring a mapping for TransactionRequest to Amount DTO in my ModelMapper. First, I assumed that simply declaring this mapping would be enough for the mapping mechanism to be also picked up when mapping a TransactionRequest to a PurchaseRequest. That however is not the case.

The second thing I tried does work, but seems to be overly complicated:

  1. I created a PropertyMap for the mapping of TransactionRequest to Amount.
  2. I created a custom converter TransactionRequest to Amount. This converter requires a ModelMapper in its constructor. The ModelMapper I pass to the constructor is the ModelMapper that created the PropertyMap from 1)
  3. I use this converter in the PropertyMaps of PurchaseRequest and CancelRequest

Here is the code:

public class AmountMap extends PropertyMap<TransactionRequest , Amount>
{
    ....

    protected void configure()
    {
      using(new LongToBigDecimalConverter(...)).map().getAmount().setValue(source.getAmount());
        }
       ...
    }
}

public class TransactionRequestToAmountConverter extends AbstractConverter<TransactionRequest, Amount>
{
    private final ModelMapper mapper;

    public TransactionRequestToAmountConverter(ModelMapper mapper)
    {
        this.mapper = mapper;
    }

    public Amount convert(TransactionRequest transactionRequest)
    {
        return mapper.map(transactionRequest, Amount.class);
    }
}

public class PurchaseRequestMap extends PropertyMap<TransactionRequest, PurchaseRequest>
{
    private final ModelMapper   mapper;

    public PurchaseRequestMap(ModelMapper mapper)
    {
        this.mapper = mapper;
    }

    protected void configure()
    {           
        using(new TransactionRequestToAmountConverter(mapper)).map(source).setAmount(null);
        ...

    }
}

Is there anybody out there that knows a simpler approach to this?

Upvotes: 0

Views: 1641

Answers (1)

Jonathan
Jonathan

Reputation: 5107

Try mapping Long to Amount by itself:

modelMapper.createTypeMap(Long.class, Amount.class).setConverter(new Converter<Long, Amount>() {
  //... convert Long to Amount
});

That mapping will then be used in your other types and should eliminate the need for the other stuff (if I'm reading your types correctly).

Upvotes: 0

Related Questions