【问题标题】:Converting a custom data structure into a bean with Spring使用 Spring 将自定义数据结构转换为 bean
【发布时间】:2017-07-22 18:38:58
【问题描述】:

我们有一个自定义数据结构,需要将其转换为 java bean。 该数据结构通常包含作为字符串的所有内容,但 bean 将具有类型化属性。

属性在数据结构和 bean 中的名称相同。

bean 的实际目标类型是在运行时确定的,为了增加趣味性,我们还需要将自定义格式的日期字符串转换为 Joda DateTime 对象。

没有为每种可能的类型编写一个方法,有什么好的解决方案呢?

由于我的代码在 Spring 容器中运行,我目前正在使用 Springs BeanWrapper,因此我不必关心标准转换(String -> BigDecimal 等)并将其配置为:

  • ConversionService + 自定义转换器
  • 自定义属性编辑器

但是,BeanWrapperImpl 有一个注释说它基本上是一个内部类,所以我对它应该如何使用或是否有替代品有点困惑。

代码如下:

import org.joda.time.DateTime;
import org.junit.Test;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;

import java.beans.PropertyEditorSupport;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

public class BeanWrapperTest
{
    private static InputDataStructure createInputDataStructure()
    {
        return new InputDataStructure(Arrays.asList(
            map(mapEntry("name", "some name"),
                mapEntry("bigDecimal", "12.34")),
            map(mapEntry("dateTime", "2017-03-02T09:30:00.0Z"))
        ));
    }

    private Map<String, String> toMap(InputDataStructure inputDataStructure)
    {
        return inputDataStructure.data.stream()
            .flatMap(innerMap -> innerMap.entrySet().stream())
            .collect(Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue
            ));
    }

    @Test
    public void copy_alternative_using_conversion_service()
    {
        // set up a conversion service and add the special date converter
        // and configure BeanWrapper with that conversion service
        DefaultConversionService defaultConversionService = new DefaultConversionService();
        defaultConversionService.addConverter(new CustomDateConverter());

        BeanWrapper beanWrapper = new BeanWrapperImpl(DestinationClass.class);
        beanWrapper.setConversionService(defaultConversionService);

        // copy all values into a Map
        // and loop over that map and use BeanWrapper to copy the properties
        Map<String, String> values = toMap(createInputDataStructure());
        values.forEach(beanWrapper::setPropertyValue);

        // bam!
        System.out.println(beanWrapper.getWrappedInstance());
    }

    @Test
    public void copy_alternative_using_formatters()
    {
        // configure BeanWrapper with a custom property editor
        BeanWrapper beanWrapper = new BeanWrapperImpl(DestinationClass.class);
        beanWrapper.registerCustomEditor(DateTime.class, new CustomDateEditor());

        // copy all values into a Map
        // and loop over that map and use BeanWrapper to copy the properties
        Map<String, String> values = toMap(createInputDataStructure());
        values.forEach(beanWrapper::setPropertyValue);

        // bam!
        System.out.println(beanWrapper.getWrappedInstance());
    }

    @SafeVarargs
    private static <K, V> Map<K, V> map(Map.Entry<K, V>... entries)
    {
        Map<K, V> map = new HashMap<>();
        for (Map.Entry<K, V> e : entries) {
            map.put(e.getKey(), e.getValue());
        }

        return map;
    }

    private static <K, V> Map.Entry<K, V> mapEntry(K key, V value)
    {
        return new AbstractMap.SimpleImmutableEntry<>(key, value);
    }

    private static class InputDataStructure
    {
        private final List<Map<String, String>> data;

        InputDataStructure(List<Map<String, String>> data)
        {
            this.data = data;
        }
    }

    private static class DestinationClass
    {
        private String name;
        private BigDecimal bigDecimal;
        private DateTime dateTime;

        public String getName()
        {
            return name;
        }

        public DestinationClass setName(String name)
        {
            this.name = name;
            return this;
        }

        public BigDecimal getBigDecimal()
        {
            return bigDecimal;
        }

        public DestinationClass setBigDecimal(BigDecimal bigDecimal)
        {
            this.bigDecimal = bigDecimal;
            return this;
        }

        public DateTime getDateTime()
        {
            return dateTime;
        }

        public DestinationClass setDateTime(DateTime dateTime)
        {
            this.dateTime = dateTime;
            return this;
        }

        @Override public String toString()
        {
            return "DestinationClass{" + "name='" + name + '\'' +
                ", bigDecimal=" + bigDecimal +
                ", dateTime=" + dateTime +
                '}';
        }
    }

    private static class CustomDateConverter implements Converter<String, DateTime>
    {
        @Override public DateTime convert(String source)
        {
            // try whatever date format would be expected
            return DateTime.parse(source);
        }
    }

    private class CustomDateEditor extends PropertyEditorSupport
    {
        @Override
        public void setAsText(String text)
        {
            // try whatever date format would be expected
            setValue(DateTime.parse(text));
        }
    }
}

【问题讨论】:

    标签: java spring


    【解决方案1】:

    有一个棘手的方法可以解决您的问题。您可以将对象转换为 json 字符串,然后使用 com.fasterxml.jackson.databind.ObjectMapper 将 json 字符串写入目标对象

    【讨论】:

      猜你喜欢
      • 2017-08-04
      • 2018-01-05
      • 2017-11-26
      • 2015-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多