Как преобразовать объект Java (bean) в пары ключ-значение (и наоборот)?

Скажем, у меня очень простой Java-объект, который имеет только некоторые свойства getXXX и setXXX. Этот объект используется только для обработки значений, в основном, для записи или карты типа (и перформанса). Мне часто приходится скрывать этот объект до ключевых пар значений (либо строк, либо типа safe), либо конвертировать из пар значений ключа в этот объект.

Помимо рефлексии или написания кода вручную для этого преобразования, каков наилучший способ достичь этого?

Примером может быть отправка этого объекта поверх jms без использования типа ObjectMessage (или преобразования входящего сообщения в нужный тип объекта).

Ответ 1

Существует всегда apache commons beanutils, но, конечно, он использует отражение под капотом

Ответ 2

Множество потенциальных решений, но добавьте еще один. Используйте Jackson (JSON processing lib) для преобразования без json-less, например:

ObjectMapper m = new ObjectMapper();
Map<String,Object> props = m.convertValue(myBean, Map.class);
MyBean anotherBean = m.convertValue(props, MyBean.class);

(в этой записи в блоге есть еще несколько примеров)

В принципе вы можете конвертировать любые совместимые типы: совместимое означает, что если вы конвертировали из типа в JSON и из этого JSON в тип результата, записи соответствовали бы (если правильно настроить, можно просто игнорировать непризнанные).

Хорошо работает для случаев, которые можно было бы ожидать, включая Карты, Списки, массивы, примитивы, bean -подобные POJO.

Ответ 3

Генерация кода была бы единственным другим способом, о котором я могу думать. Лично я получил решение, обычно используемое для повторного использования (если только эта часть кода не является абсолютно критичной для производительности). Использование JMS звучит как overkill (дополнительная зависимость, и это даже не то, что это означает). Кроме того, он, вероятно, использует отражение также под капотом.

Ответ 4

Это метод преобразования объекта Java в Map

public static Map<String, Object> ConvertObjectToMap(Object obj) throws 
    IllegalAccessException, 
    IllegalArgumentException, 
    InvocationTargetException {
        Class<?> pomclass = obj.getClass();
        pomclass = obj.getClass();
        Method[] methods = obj.getClass().getMethods();


        Map<String, Object> map = new HashMap<String, Object>();
        for (Method m : methods) {
           if (m.getName().startsWith("get") && !m.getName().startsWith("getClass")) {
              Object value = (Object) m.invoke(obj);
              map.put(m.getName().substring(3), (Object) value);
           }
        }
    return map;
}

Вот как это называется

   Test test = new Test()
   Map<String, Object> map = ConvertObjectToMap(test);

Ответ 5

С помощью Java 8 вы можете попробовать следующее:

public Map<String, Object> toKeyValuePairs(Object instance) {
    return Arrays.stream(Bean.class.getDeclaredMethods())
            .collect(Collectors.toMap(
                    Method::getName,
                    m -> {
                        try {
                            Object result = m.invoke(instance);
                            return result != null ? result : "";
                        } catch (Exception e) {
                            return "";
                        }
                    }));
}

Ответ 6

JSON, например, с помощью XStream + Jettison, представляет собой простой текстовый формат с парами значений ключа. Он поддерживается, например, брокером сообщений Apache ActiveMQ JMS для обмена объектами Java с другими платформами/языками.

Ответ 7

Лучшим решением является использование Dozer. Вам просто нужно что-то подобное в файле mapper:

<mapping map-id="myTestMapping">
  <class-a>org.dozer.vo.map.SomeComplexType</class-a>
  <class-b>java.util.Map</class-b>
</mapping> 

И что он, Дозер заботится обо всем остальном!!!

URL-адрес документации к Dozer

Ответ 8

Просто используя отражение и Groovy:

def Map toMap(object) {             
return object?.properties.findAll{ (it.key != 'class') }.collectEntries {
            it.value == null || it.value instanceof Serializable ? [it.key, it.value] : [it.key,   toMap(it.value)]
    }   
}

def toObject(map, obj) {        
    map.each {
        def field = obj.class.getDeclaredField(it.key)
        if (it.value != null) {
            if (field.getType().equals(it.value.class)){
                obj."$it.key" = it.value
            }else if (it.value instanceof Map){
                def objectFieldValue = obj."$it.key"
                def fieldValue = (objectFieldValue == null) ? field.getType().newInstance() : objectFieldValue
                obj."$it.key" = toObject(it.value,fieldValue) 
            }
        }
    }
    return obj;
}

Ответ 9

Используйте juffrou-reflect BeanWrapper. Это очень показательно.

Вот как вы можете преобразовать bean в карту:

public static Map<String, Object> getBeanMap(Object bean) {
    Map<String, Object> beanMap = new HashMap<String, Object>();
    BeanWrapper beanWrapper = new BeanWrapper(BeanWrapperContext.create(bean.getClass()));
    for(String propertyName : beanWrapper.getPropertyNames())
        beanMap.put(propertyName, beanWrapper.getValue(propertyName));
    return beanMap;
}

Я сам разработал Джуффру. Это с открытым исходным кодом, поэтому вы можете использовать его и изменять. И если у вас есть какие-либо вопросы по этому поводу, я буду более чем счастлив ответить.

Приветствия

Карлос

Ответ 10

При использовании Spring можно также использовать Spring Integration object-to-map-transformer. Вероятно, не стоит добавлять Spring в качестве зависимости только для этого.

Для документации найдите "Трансформатор объектов к карте" на http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html

По существу, он пересекает весь объектный граф, достижимый из объекта, заданного как вход, и создает карту из всех полей примитивного типа/Строка на объектах. Его можно настроить для вывода:

  • плоская карта: {rootObject.someField = Joe, rootObject.leafObject.someField = Jane} или
  • структурированная карта: {someField = Joe, leafObject = {someField = Jane}}.

Вот пример со своей страницы:

public class Parent{
    private Child child;
    private String name; 
    // setters and getters are omitted
}

public class Child{
   private String name; 
   private List<String> nickNames;
   // setters and getters are omitted
}

Выход будет:

{person.name = George, person.child.name = Jenna, person.child.nickNames [0] = Bimbo., и т.д.}

Также доступен обратный трансформатор.

Ответ 11

Вы можете использовать фреймворк Joda:

http://joda.sourceforge.net/

и воспользоваться преимуществами JodaProperties. Это означает, что вы создаете beans определенным образом и реализуете определенный интерфейс. Однако он позволяет вернуть карту свойств из определенного класса без отражения. Пример кода здесь:

http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57

Ответ 12

Если вы не хотите делать жесткие коды для каждого получателя и сеттера, отражение является единственным способом вызова этих методов (но это не сложно).

Можете ли вы реорганизовать рассматриваемый класс, чтобы использовать объект Properties для хранения фактических данных, и пусть каждый геттер и сеттер просто вызывают get/set на нем? Тогда у вас есть структура, хорошо подходящая для того, что вы хотите сделать. Существуют даже методы сохранения и загрузки их в форме ключа.

Ответ 13

Конечно, возможно абсолютное простейшее средство конверсии - никакого преобразования вообще!

вместо использования частных переменных, определенных в классе, сделать класс содержащим только HashMap, который хранит значения для экземпляра.

Затем ваши получатели и сеттеры возвращаются и устанавливают значения в и из HashMap, и когда пришло время преобразовать его в карту, вуаля! - это уже карта.

С небольшим количеством AOP-волшебства вы можете даже поддерживать негибкость, присущую bean, позволяя вам по-прежнему использовать геттеры и сеттеры, специфичные для каждого имени значений, без необходимости писать отдельные геттеры и сеттеры.

Ответ 14

Вы можете использовать свойства коллектора фильтра потока java 8,

public Map<String, Object> objectToMap(Object obj) {
    return Arrays.stream(YourBean.class.getDeclaredMethods())
            .filter(p -> !p.getName().startsWith("set"))
            .filter(p -> !p.getName().startsWith("getClass"))
            .filter(p -> !p.getName().startsWith("setClass"))
            .collect(Collectors.toMap(
                    d -> d.getName().substring(3),
                    m -> {
                        try {
                            Object result = m.invoke(obj);
                            return result;
                        } catch (Exception e) {
                            return "";
                        }
                    }, (p1, p2) -> p1)
            );
}

Ответ 15

My JavaDude Bean Обработчик аннотации генерирует код для этого.

http://javadude.googlecode.com

Например:

@Bean(
  createPropertyMap=true,
  properties={
    @Property(name="name"),
    @Property(name="phone", bound=true),
    @Property(name="friend", type=Person.class, kind=PropertyKind.LIST)
  }
)
public class Person extends PersonGen {}

Вышеописанное создает суперкласс класса PersonGen, который включает метод createPropertyMap(), который генерирует карту для всех свойств, определенных с помощью @ Bean.

(Обратите внимание, что я немного изменяю API для следующей версии - атрибут аннотации будет defineCreatePropertyMap = true)

Ответ 16

Вы должны написать универсальную услугу преобразования! Используйте generics, чтобы сохранить его без шрифта (чтобы вы могли конвертировать каждый объект в значение key = > и обратно).

Какое поле должно быть ключом? Получите это поле из bean и добавьте любое другое непереходное значение в карте значений.

Обратный путь довольно прост. Прочитайте ключ (x) и сначала напишите ключ, а затем все записи списка вернитесь к новому объекту.

Вы можете получить имена свойств bean с apache commons beanutils!

Ответ 17

Если речь идет о простом дереве объектов для сопоставления списков значений ключей, где ключ может представлять собой описание с точками пути из корневого элемента объекта в проверяемый лист, довольно очевидно, что преобразование дерева в список ключевых значений сопоставимый с объектом для сопоставления xml. Каждый элемент документа XML имеет определенную позицию и может быть преобразован в путь. Поэтому я взял XStream в качестве базового и стабильного инструмента преобразования и заменил иерархическую часть драйвера и записи собственной реализацией. XStream также имеет базовый механизм отслеживания траектории, который, будучи объединенным с другими двумя, строго приводит к тому, что решение подходит для задачи.

Ответ 18

Если вам действительно нужна производительность, вы можете пойти по пути генерации кода.

Вы можете сделать это на своем, делая свое собственное отражение и создавая mixin AspectJ ITD.

Или вы можете использовать Spring Roo и создать Spring Roo Addon. Ваш аддон Roo сделает что-то подобное выше, но будет доступен всем, кто использует Spring Roo, и вам не нужно использовать аннотации времени выполнения.

Я сделал оба. Люди дерутся на Spring Roo, но это действительно самое полное генерирование кода для Java.

Ответ 19

Здесь возможен еще один возможный путь.

BeanWrapper предлагает функциональные возможности для установки и получения значений свойств (индивидуально  или навалом), получить дескрипторы свойств и запросить свойства, чтобы определить,  читабельны или доступны для записи.

Company c = new Company();
 BeanWrapper bwComp = BeanWrapperImpl(c);
 bwComp.setPropertyValue("name", "your Company");

Ответ 20

С помощью библиотеки Джексона мне удалось найти все свойства класса типа String/integer/double и соответствующие значения в классе Map. (без использования отражений api!)

TestClass testObject = new TestClass();
com.fasterxml.jackson.databind.ObjectMapper m = new com.fasterxml.jackson.databind.ObjectMapper();

Map<String,Object> props = m.convertValue(testObject, Map.class);

for(Map.Entry<String, Object> entry : props.entrySet()){
    if(entry.getValue() instanceof String || entry.getValue() instanceof Integer || entry.getValue() instanceof Double){
        System.out.println(entry.getKey() + "-->" + entry.getValue());
    }
}

Ответ 21

Наверное, опоздал на вечеринку. Вы можете использовать Джексона и преобразовать его в объект Properties. Это подходит для вложенных классов, и если вы хотите ввести значение для abc = value.

JavaPropsMapper mapper = new JavaPropsMapper();
Properties properties = mapper.writeValueAsProperties(sct);
Map<Object, Object> map = properties;

если вам нужен суффикс, просто сделайте

SerializationConfig config = mapper.getSerializationConfig()
                .withRootName("suffix");
mapper.setConfig(config);

нужно добавить эту зависимость

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-properties</artifactId>
</dependency>

Ответ 22

Используя Gson,

        retMap = new Gson().fromJson(new Gson().toJson(object), new TypeToken<HashMap<String, Object>>() {
                }.getType()