Powiedzmy, że mam bardzo prosty obiekt Java, który ma tylko niektóre właściwości getXXX i setXXX. Ten obiekt jest używany tylko do obsługi wartości, w zasadzie rekordu lub bezpiecznej dla typu (i wydajnej) mapy. Często muszę ukrywać ten obiekt w parach klucz-wartość (ciągi znaków lub typ bezpieczny) lub konwertować z par klucz-wartość do tego obiektu.
Oprócz odbicia lub ręcznego pisania kodu w celu wykonania tej konwersji, jaki jest najlepszy sposób na osiągnięcie tego?
Przykładem może być wysłanie tego obiektu przez jms, bez użycia typu ObjectMessage (lub konwersja przychodzącej wiadomości na odpowiedni rodzaj obiektu).
java
reflection
collections
javabeans
Shahbaz
źródło
źródło
java.beans.Introspector
.getBeanInfo()
. Jest wbudowany w JDK.Odpowiedzi:
Zawsze są fasolki zwyczajne Apache commons, ale oczywiście wykorzystuje odbicie pod maską
źródło
java.beans
zależności mocno ograniczonych na platformie. Szczegółowe informacje można znaleźć w powiązanym pytaniu .Wiele potencjalnych rozwiązań, ale dodajmy jeszcze jedno. Użyj Jackson (biblioteka przetwarzania JSON), aby wykonać konwersję „bez json”, na przykład:
ObjectMapper m = new ObjectMapper(); Map<String,Object> props = m.convertValue(myBean, Map.class); MyBean anotherBean = m.convertValue(props, MyBean.class);
( ten wpis na blogu zawiera więcej przykładów)
Możesz w zasadzie przekonwertować dowolne zgodne typy: zgodne, co oznacza, że jeśli przekonwertujesz z typu na JSON iz tego JSON na typ wyniku, wpisy będą pasować (jeśli skonfigurowane poprawnie, możesz po prostu zignorować nierozpoznane).
Działa dobrze w przypadkach, których można by się spodziewać, w tym w mapach, listach, tablicach, prymitywach, POJO podobnych do fasoli.
źródło
BeanUtils
jest fajny, ale nie radzi sobie z tablicami i wyliczeniamiGenerowanie kodu to jedyny inny sposób, jaki przychodzi mi do głowy. Osobiście dostałem z generalnie rozwiązaniem do wielokrotnego użytku (chyba że ta część kodu jest absolutnie krytyczna dla wydajności). Używanie JMS brzmi jak przesada (dodatkowa zależność, a nawet nie do tego służy). Poza tym zapewne wykorzystuje odbicia także pod maską.
źródło
Jest to metoda konwersji obiektu Java na 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; }
Oto jak to nazwać
Test test = new Test() Map<String, Object> map = ConvertObjectToMap(test);
źródło
Pewnie spóźniony na przyjęcie. Możesz użyć Jacksona i przekonwertować go na obiekt Właściwości. Jest to odpowiednie dla klas zagnieżdżonych i jeśli chcesz mieć klucz w for abc = value.
JavaPropsMapper mapper = new JavaPropsMapper(); Properties properties = mapper.writeValueAsProperties(sct); Map<Object, Object> map = properties;
jeśli chcesz jakiś przyrostek, po prostu zrób
SerializationConfig config = mapper.getSerializationConfig() .withRootName("suffix"); mapper.setConfig(config);
trzeba dodać tę zależność
źródło
W Javie 8 możesz spróbować tego:
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 ""; } })); }
źródło
JSON , na przykład przy użyciu XStream + Jettison, to prosty format tekstowy z parami klucz-wartość. Jest obsługiwany na przykład przez broker komunikatów Apache ActiveMQ JMS do wymiany obiektów Java z innymi platformami / językami.
źródło
Po prostu używając Refleksji i 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; }
źródło
Użyj BeanWrapper firmy juffrou- reflect. Jest bardzo wydajny.
Oto, jak możesz przekształcić fasolę w mapę:
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; }
Sam opracowałem Juffrou. Jest to oprogramowanie typu open source, więc możesz go używać i modyfikować. Jeśli masz jakieś pytania, z przyjemnością odpowiem.
Twoje zdrowie
Carlos
źródło
Korzystając ze Springa, można również użyć transformatora obiekt-mapa-integracji Spring Integration. Prawdopodobnie nie warto dodawać Springa jako zależności tylko w tym celu.
Aby uzyskać dokumentację, wyszukaj „Object-to-Map Transformer” na http://docs.spring.io/spring-integration/docs/4.0.4.RELEASE/reference/html/messaging-transformation-chapter.html
Zasadniczo przechodzi przez cały graf obiektu osiągalny z obiektu podanego jako dane wejściowe i tworzy mapę ze wszystkich pól typu pierwotnego / String na obiektach. Można go skonfigurować tak, aby wyświetlał:
Oto przykład z ich strony:
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 }
Wynik będzie:
Dostępny jest również transformator zwrotny.
źródło
Możesz użyć frameworka Joda:
http://joda.sourceforge.net/
i skorzystaj z JodaProperties. Jednak wymaga to tworzenia fasoli w określony sposób i zaimplementowania określonego interfejsu. Pozwala to jednak zwrócić mapę właściwości z określonej klasy bez refleksji. Przykładowy kod jest tutaj:
http://pbin.oogly.co.uk/listings/viewlistingdetail/0e78eb6c76d071b4e22bbcac748c57
źródło
Jeśli nie chcesz zakodować na stałe wywołań każdego gettera i settera, odbicie jest jedynym sposobem wywołania tych metod (ale nie jest to trudne).
Czy możesz refaktoryzować klasę, o której mowa, tak, aby używała obiektu Properties do przechowywania rzeczywistych danych i pozwalała każdemu pobierającemu i ustawiającemu wywoływać po prostu get / set? Wtedy masz strukturę dobrze dopasowaną do tego, co chcesz robić. Istnieją nawet metody zapisywania i ładowania ich w postaci klucza-wartości.
źródło
Najlepszym rozwiązaniem jest użycie Dozera. Potrzebujesz tylko czegoś takiego w pliku mapowania:
<mapping map-id="myTestMapping"> <class-a>org.dozer.vo.map.SomeComplexType</class-a> <class-b>java.util.Map</class-b> </mapping>
I to wszystko, Dozer zajmie się resztą !!!
Adres URL dokumentacji Dozera
źródło
Oczywiście istnieje absolutnie najprostszy możliwy sposób nawrócenia - żadnego nawrócenia!
zamiast używać prywatnych zmiennych zdefiniowanych w klasie, spraw, aby klasa zawierała tylko HashMap, który przechowuje wartości dla instancji.
Następnie twoje gettery i settery zwracają i ustawiają wartości do i z HashMap, a kiedy nadejdzie czas, aby przekonwertować je na mapę, voila! - to już mapa.
Przy odrobinie magii AOP, można by nawet zachować sztywność nieodłączną od fasoli, pozwalając nadal używać metod pobierających i ustawiających specyficznych dla każdej nazwy wartości, bez konieczności faktycznego pisania poszczególnych metod pobierających i ustawiających.
źródło
Możesz użyć właściwości kolektora filtru strumienia 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) ); }
źródło
Mój procesor adnotacji JavaDude Bean generuje kod, aby to zrobić.
http://javadude.googlecode.com
Na przykład:
@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 {}
Powyższe generuje PersonGen z nadklasy, która zawiera metodę createPropertyMap (), która generuje Mapę dla wszystkich właściwości zdefiniowanych przy użyciu @Bean.
(Zwróć uwagę, że nieznacznie zmieniam interfejs API dla następnej wersji - atrybut adnotacji będzie miał wartość defineCreatePropertyMap = true)
źródło
Powinieneś napisać ogólną usługę transformacji! Użyj typów ogólnych, aby zachować swobodny typ (dzięki czemu możesz przekonwertować każdy obiekt na klucz => wartość iz powrotem).
Jakie pole powinno być kluczowe? Pobierz to pole z fasoli i dołącz dowolną inną wartość trwałą do mapy wartości.
Droga powrotna jest całkiem łatwa. Przeczytaj klucz (x) i napisz najpierw klucz, a następnie każdą pozycję listy z powrotem do nowego obiektu.
Możesz uzyskać nazwy właściwości fasoli za pomocą apache commons beanutils !
źródło
Jeśli naprawdę zależy Ci na wydajności, możesz skorzystać z trasy generowania kodu.
Możesz to zrobić samodzielnie, wykonując własne przemyślenia i budując mieszankę AspectJ ITD.
Lub możesz użyć Spring Roo i stworzyć dodatek Spring Roo . Twój dodatek Roo zrobi coś podobnego do powyższego, ale będzie dostępny dla każdego, kto używa Spring Roo i nie musisz używać adnotacji w czasie wykonywania.
Zrobiłem jedno i drugie. Ludzie bzdurają na Spring Roo, ale tak naprawdę jest to najbardziej wszechstronne generowanie kodu dla Java.
źródło
Jest jeszcze jeden możliwy sposób.
BeanWrapper oferuje funkcje ustawiania i pobierania wartości właściwości (indywidualnie lub zbiorczo), pobierania deskryptorów właściwości oraz wysyłania zapytań o właściwości w celu określenia, czy są one czytelne, czy zapisywalne.
Company c = new Company(); BeanWrapper bwComp = BeanWrapperImpl(c); bwComp.setPropertyValue("name", "your Company");
źródło
Jeśli chodzi o mapowanie prostego drzewa obiektów na listę wartości kluczy, gdzie klucz może być kropkowanym opisem ścieżki od elementu głównego obiektu do liścia poddawanego inspekcji, jest raczej oczywiste, że konwersja drzewa na listę klucz-wartość jest porównywalna do obiekt do mapowania XML. Każdy element w dokumencie XML ma zdefiniowaną pozycję i można go przekształcić w ścieżkę. Dlatego wziąłem XStream jako podstawowe i stabilne narzędzie do konwersji i zastąpiłem hierarchiczne części sterownika i pisarza własną implementacją. XStream jest również wyposażony w podstawowy mechanizm śledzenia ścieżki, który - w połączeniu z pozostałymi dwoma - ściśle prowadzi do rozwiązania odpowiedniego do zadania.
źródło
Z pomocą biblioteki Jacksona udało mi się znaleźć wszystkie właściwości klas typu String / integer / double i odpowiednie wartości w klasie Map. ( bez używania interfejsu API odbić! )
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()); } }
źródło
Korzystając z Gson,
object
na JsonKonwertuj Json na Map
retMap = new Gson().fromJson(new Gson().toJson(object), new TypeToken<HashMap<String, Object>>() {}.getType() );
źródło
Możemy użyć biblioteki Jacksona, aby łatwo przekonwertować obiekt Java na mapę.
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.3</version> </dependency>
Jeśli używasz w projekcie systemu Android, możesz dodać jackson w pliku build.gradle swojej aplikacji w następujący sposób:
implementation 'com.fasterxml.jackson.core:jackson-core:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.8' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
Przykładowe wdrożenie
public class Employee { private String name; private int id; private List<String> skillSet; // getters setters } public class ObjectToMap { public static void main(String[] args) { ObjectMapper objectMapper = new ObjectMapper(); Employee emp = new Employee(); emp.setName("XYZ"); emp.setId(1011); emp.setSkillSet(Arrays.asList("python","java")); // object -> Map Map<String, Object> map = objectMapper.convertValue(emp, Map.class); System.out.println(map); } }
Wynik:
{nazwa = XYZ, id = 1011, skills = [python, java]}
źródło