【问题标题】:custom dozer mapping自定义推土机映射
【发布时间】:2011-04-21 13:58:00
【问题描述】:

我正在尝试使用 Dozer 来转换一个实例

class Source {
  private List<Foo> foos = new ArrayList<Foo>();

  public List<Foo> getFoos() {
    return foos;
  }

  public void setFoos(List<Foo> foos) {
    this.foos = foos;
  }
}

到一个实例:

class Target {
  private List<Foo> foos = new ArrayList<Foo>();

  public List<Foo> getFoos() {
    return foos;
  }
}

在 Java 代码中,我会像这样进行转换

Source s = new Source();
Target t = new Target();
t.getFoos().addAll(s.getFoos());

默认情况下,Dozer 不执行此转换,因为 Target 没有 foos 属性(只是一个 getter)。

实际上,我有很多这样的属性需要映射。一种选择是将 Dozer 告诉map the private fields directly,但这并不完全令人满意,因为:

  • 我需要在 Dozer XML 配置中通过名称指定每个要以这种方式映射的字段
  • 访问私有字段是不好的

有没有更好的办法?

【问题讨论】:

  • 您知道t.getFoos().addAll(s.getFoos()); 会导致上述Target 实现的NPE,不是吗?
  • 对不起,我在“示例”我的代码时犯了一个错误。我已经修好了

标签: java dozer


【解决方案1】:

除了is-accessible 标志之外,没有简单的方法可以解决这个问题。 但是您可以定义一个使用 getter 的自定义转换器:

t.getFoos().addAll(s.getFoos());

这将是非常繁重的工作。您需要在Source 和Target 之间定义一个自定义转换器(参见http://dozer.sourceforge.net/documentation/customconverter.html),它使用getter 而不是setter:

public class TestCustomConverter implements CustomConverter {

  public Object convert(Object destination, Object source, Class destClass, Class sourceClass) {
    if (source == null) {
      return null;
    }
    if (source instanceof Source) {
      Target dest = null;
      // check to see if the object already exists
      if (destination == null) {
        dest = new Target();
      } else {
        dest = (Target) destination;
      }
      dest.getFoos().addAll(((Source)source).getFoos());
      return dest;
    } else if (source instanceof Target) {
      Source dest = null;
      // check to see if the object already exists
      if (destination == null) {
        dest = new Source();
      } else {
        dest = (Source) destination;
      }
      dest.setFoos(((Target)source).getFoos());
      return dest;
    } else {
      throw new MappingException("Converter TestCustomConverter used incorrectly. Arguments passed in were:"
          + destination + " and " + source);
    }
  } 

我想,祝你好运

【讨论】:

  • 现在我已经修复了 NPE,您能否展示我如何使用自定义转换器来解决这个问题?
  • 感谢您的帮助,但上述解决方案仅适用于将Source.foos 映射到Target.foos。我需要一个解决方案,它可以映射任何两个 List 类型的属性,其中目标类中只有一个 getter。
  • @don 这是真的,但你不能一般地映射列表,因为它们需要父类的知识才能使用 getter。这是我能想到的唯一解决方案。
【解决方案2】:

您可以向 Target 添加一个方法:

public void addFoo(Foo item) {
    foos.add(item);
}

<mapping>
  <class-a>Source</class-a>
  <class-b>Target</class-b>
  <field>
    <a>foos</a>
    <b set-method="addFoo" type="iterate">foos</b>
  </field>
</mapping>

也许建议一个允许在 setter 或 getter 表达式中使用 EL 的功能

【讨论】:

    【解决方案3】:

    您可以查看 ModelMapper 作为 Dozer 的替代品。处理这种情况很简单:

    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration()
      .enableFieldMatching(true)
      .setFieldAccessLevel(AccessLevel.PRIVATE);
    

    这为 all 私有字段启用了字段匹配。执行映射:

    Target target = modelMapper.map(source, Target.class);
    

    查看 ModelMapper 网站了解更多信息:

    http://modelmapper.org

    【讨论】:

      【解决方案4】:

      例如,如果您没有列表值的设置器(就像我出于某种原因那样...),您可以将字段映射与“this”结合使用,以识别您可以使用“key”的属性:

      <field custom-converter="de.xyz.custom.MyConverter">
          <a key="variablename">this</a>
          <b>targetvariablename</b>
      </field>
      

      然后您可以继续实施转换器。您将获得包含字段“variablename”作为源的对象。您现在可以按照您需要的方式操作源对象(在这种情况下,使用 getter、获取列表、addAll() 就可以了)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多