【问题标题】:With dozer is it possible to map several fields to one field?使用推土机是否可以将多个字段映射到一个字段?
【发布时间】:2015-12-15 19:30:42
【问题描述】:

我们有一些我们正在尝试映射的旧数据...旧数据具有月日年字段...

是否可以转换

MyObject.day
MyObject.year
MyObject.month

MyOtherObject.date

我找不到有关此主题的任何文档。任何将不胜感激。

【问题讨论】:

    标签: java dozer


    【解决方案1】:

    我知道这是一个旧帖子,但我找不到令人满意的答案,花了很多时间才发现这个(我认为)简单的方法。您可以将 ConfigurableCustomConver 与 mapping.xml 中的“this”引用结合使用。

    例如:

    public class Formatter implements ConfigurableCustomConverter
    {
       private String format;
       private String[] fields;
    
       @Override
       public Object convert(Object existingDestinationFieldValue, Object           sourceFieldValue, Class<?> destinationClass, Class<?> sourceClass) {
          List valueList = new ArrayList();
    
          for (String field : fields){
            try {
               valueList.add(sourceClass.getMethod(field).invoke(sourceFieldValue));
            }
            catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
               throw new RuntimeException("Reflection error during mapping", e);
            }
         }
         return MessageFormat.format(format, valueList.toArray());
      }
    
      @Override
      public void setParameter(String parameter)
      {
         String[] parameters = parameter.split("\\|");
         format = parameters[0];
         fields = Arrays.copyOfRange( parameters, 1, parameters.length);
      }
    }
    

    在你的 mapping.xml 中:

    <mapping type="one-way">
        <class-a>test.model.TestFrom</class-a>
        <class-b>test.model.TestTo</class-b>
    
        <field custom-converter="nl.nxs.dozer.Formatter" custom-converter-param="{0}{1}|getFirstValue|getSecondValue">
            <a>this</a>
            <b>combinedValue</b>
        </field>
    </mapping>
    

    类:

    public class TestFrom
    {
       private String firstValue;
       private String secondValue;
    
       public String getFirstValue()
       {
          return firstValue;
       }
    
       public void setFirstValue(String firstValue)
       {
          this.firstValue = firstValue;
       }
    
       public String getSecondValue()
       {
          return secondValue;
       }
    
       public void setSecondValue(String secondValue)
       {
          this.secondValue = secondValue;
       } 
    }
    public class TestTo
    {
       private String combinedValue;  
    
       public String getCombinedValue(){
          return combinedValue;
       } 
    
       public void setCombinedValue(String combinedValue){
          this.combinedValue = combinedValue;
       }
    }
    

    【讨论】:

    • 这是我提出这个问题时所需要的一个很好的例子。
    • 为了完整起见,很高兴看到 TestFrom TestTo .. 虽然我们了解这个库,但它不像以前那样普遍了。
    • 为了完整性,我添加了缺失的类。
    【解决方案2】:

    如果您需要将两个字段映射到单个字段,反之亦然,我会推荐一些不同的解决方案。

    Dozer 可以使用用户 setter/getter 来进行映射。在您必须执行此操作的类上(您有两个字段并希望映射到单个字段)使用 setter,您可以在其中提供来自不同对象的单个字段,并在 setter/getter 逻辑上分配这两个字段。

    示例映射:

    <mapping type="bi-directional">
    <class-a>ClassWithTwoFields</class-a>
    <class-b>ClassWithSingleField</class-b>
    <field>
      <a get-method="getHelperField" set-method="setHelperField" >helperField</a>
      <b>helperField</b>
    </field>
    

    getter/setter 示例:

     public FieldType getHelperField() {
        if (!isNull(field1) && !isNull(field2)) {
            return field1 + field2;
        }
        return null;
    }
    
    public void setHelperField(FieldType singleField) {
        if (!isNull(singleField)) {
            this.field1 = singleField.part1();
            this.field2 = singleField.part2();
        }
    }
    

    这是解决问题的快速方法。

    【讨论】:

    • 我最终对对象进行了子类型化并做这件事。为系统的其余部分保持基础对象的清洁和美观,并将其视为转换器类
    • 这是一种解决方案,不幸的是,如果您操作的类是生成的(例如从 xsd 生成),则不是。在这种情况下,请参阅下面的解决方案。
    【解决方案3】:

    根据Dozer documentation

    如何将多个字段映射到一个字段?

    Dozer 目前不支持此功能。由于周围的复杂性?实施它,此功能目前不在路线图上。一种可能的解决方案是将多个字段包装在自定义复杂类型中,然后定义自定义转换器以在复杂类型和单个字段之间进行映射。这样,您可以处理将三个字段映射到自定义转换器中的单个字段所需的自定义逻辑。

    如果您可以控制旧数据,则可以创建一个类型来包装各个日期字段:

    MyWrapperObject.java

    public class MyWrapperObject {
    
        private int day;
    
        private int month;
    
        private int year;
    
    }
    

    MyObject.java

    public class MyObject {
    
        private MyWrapperObject myWrapperObject;
    
    }
    

    然后使用自定义转换器将 3 个字段映射为日期字段:

    MyOtherWrapperObject.java

    public class MyOtherWrapperObject {
    
        private Date date;
    
    }
    

    DateCustomConverter .java

    public class DateCustomConverter implements CustomConverter {
    
        @Override
        public Object convert(Object destination, Object source,
                @SuppressWarnings("rawtypes") Class destClass,
                @SuppressWarnings("rawtypes") Class sourceClass) {
    
            // Source object is null
            if (source == null) {
                return null;
            }
    
            if (source instanceof MyWrapperObject) {
                MyOtherWrapperObject dest = new MyOtherWrapperObject();
                MyWrapperObject src = (MyWrapperObject) source;
    
                Calendar c = Calendar.getInstance();
                // Months are 0 based in Java 
                c.set(src.getYear(), src.getMonth() - 1, src.getDay());
                dest.setDate(c.getTime());
    
                return dest;
            }
            return null;
        }
    

    然后在您的 XML 映射文件中引用此转换器:

    dozer.xml

    <mapping  map-null="false" >
        <class-a>com.xxx.MyObject</class-a>
        <class-b>com.xxx.MyOtherObject</class-b>
    
        ...
    
        <field custom-converter="com.xxx.DateCustomConverter">
            <a>myWrapperObject</a>
            <b>myOtherWrapperObject</b>
        </field>
    </mapping>
    

    如果您无法控制旧数据,那么我相信您必须编写一个自定义映射器来将 MyObject 映射到 MyOtherObject

    【讨论】:

    • 不喜欢这个答案,但它是准确的,哈哈谢谢。实施起来会很好,但我确实看到了复杂性。
    【解决方案4】:

    这可能与问题 100% 不匹配,但我来到这里试图找到这个解决方案(可能还有许多其他解决方案)。这是做什么的:将单个字段值映射到目标类中的两个字段,条件是要处理哪个目标。想象一个单一的“名称”字段,该字段应映射到目标类中的名称和名称缩写字段。 我正在使用推土机“ConfigurableCustomConverter”。

    映射:

    <field custom-converter="de.foo.MyConfCustomConverter" custom-converter-param="name">
      <a>person.name</a>
      <b>student.name</b>
    </field>
    <field custom-converter="de.foo.MyConfCustomConverter" custom-converter-param="abbreviation">
      <a>person.name</a>
      <b>student.shortName</b>
    </field>
    

    匹配的自定义转换器:

    public class MyConfCustomConverter implements ConfigurableCustomConverter {
    
    private String param;
    
    @SuppressWarnings("rawtypes")
    @Override
    public Object convert(Object destination, Object source, Class destClass, Class sourceClass) {
        if ("name".equals(this.param)) {
            return (String) source;
        }
        else if ("abbreviation".equals(this.param)) {
            return myAbbrevFunction((String) source);
        }
        return null;
    }
    
    @Override
    public final void setParameter(String parameter) {
        this.param = parameter;
    }
    
    }
    

    如果您有要映射的其他复杂类型,您还可以实现“MapperAware”以在您的 CustomConverter 中使用 Dozer Mapper 函数:

    public class MyConfCustomConverter implements ConfigurableCustomConverter, MapperAware{
    
    private String param;
    private Mapper mapper;
    
    @SuppressWarnings("rawtypes")
    @Override
    public Object convert(Object destination, Object source, Class destClass, Class sourceClass) {
        if ("name".equals(this.param)) {
            return (String) source;
        }
        else if ("abbreviation".equals(this.param)) {
            if(isSourceClassAComplexType()){
                return mapper.map(source, my.target.package.ComplexType.class);
            }
            return myAbbrevFunction((String) source);
        }
        return null;
    }
    
    @Override
    public final void setParameter(String parameter) {
        this.param = parameter;
    }
    
    }
    

    【讨论】:

    • 另一种解决方案是编写一个转换器并将“this”提供给转换器,以便您可以访问转换器中源类的字段。但是,此时您正在有效地解决推土机问题,这只能作为绝对例外来完成。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多