2011-07-22 31 views
31

Tôi có một đối tượng Java obj có thuộc tính obj.attr1, obj.attr2 vv Các thuộc tính được có thể truy cập thông qua một cấp thêm về mình: obj.getAttr1(), obj.getAttr2(), nếu không được công khai.Java mẫn: đối tượng để lập bản đồ

Thách thức: Tôi muốn có một chức năng mà phải mất một đối tượng, và trả về một Map<String, Object>, nơi các phím là chuỗi "attr1", "attr2" vv và giá trị này là các đối tượng tương ứng obj.attr1, obj.attr2. Tôi tưởng tượng chức năng sẽ được gọi với một cái gì đó giống như

  • toMap(obj),
  • hoặc toMap(obj, "attr1", "attr3") (nơi attr1attr3 là một tập hợp con của obj 's thuộc tính),
  • hoặc có lẽ toMap(obj, "getAttr1", "getAttr3") nếu cần thiết.

Tôi không biết nhiều về nội tâm của Java: làm cách nào bạn làm điều đó trong Java?

Hiện tại, tôi có một triển khai chuyên ngành toMap() cho từng loại đối tượng mà tôi quan tâm và quá nhiều bản mẫu.


LƯU Ý: dành cho những ai biết Python, tôi muốn một cái gì đó giống như obj.__dict__. Hoặc dict((attr, obj.__getattribute__(attr)) for attr in attr_list) cho biến thể tập hợp con.

+0

Có phải đệ quy không? – biziclop

+0

@biziclop: không, không có đệ quy – Radim

Trả lời

22

Bạn có thể sử dụng JavaBeans nội tâm cho việc này. Đọc trên lớp java.beans.Introspector:

public static Map<String, Object> introspect(Object obj) throws Exception { 
    Map<String, Object> result = new HashMap<String, Object>(); 
    BeanInfo info = Introspector.getBeanInfo(obj.getClass()); 
    for (PropertyDescriptor pd : info.getPropertyDescriptors()) { 
     Method reader = pd.getReadMethod(); 
     if (reader != null) 
      result.put(pd.getName(), reader.invoke(obj)); 
    } 
    return result; 
} 

Big caveat: giao dịch Mã của tôi chỉ với phương pháp getter; nó sẽ không tìm thấy những cánh đồng trần truồng. Đối với các trường, xem câu trả lời của highcaffeinated. :-) (Có thể bạn sẽ muốn kết hợp hai cách tiếp cận này.)

+0

Whats sự khác biệt giữa Introspector vs chỉ phản ánh? –

+0

@Amir: Introspection sử dụng sự phản chiếu để thực hiện công việc của mình, nhưng hoạt động ở mức cao hơn so với sự phản chiếu. Phản ánh tìm các trường và phương thức mức Java; tìm kiếm nội dung và các sự kiện cấp JavaBeans. –

+0

Tất cả các câu trả lời đều tốt. Tôi sẽ chấp nhận điều này vì nó đã chứng tỏ hữu ích nhất đối với tôi trong giải pháp cuối cùng của tôi (bao gồm 'BeanInfo' và' java.io.Serializable'). Cảm ơn tất cả. – Radim

5

Đây là cách thực hiện thực sự dễ dàng để thực hiện việc này.

Sử dụng Jackson JSON lib để chuyển đổi đối tượng thành JSON.

Sau đó đọc JSON và chuyển đổi nó thành Bản đồ.

Bản đồ sẽ chứa mọi thứ bạn muốn.

Dưới đây là 4 lót

ObjectMapper om = new ObjectMapper(); 
StringWriter sw = new StringWriter(); 
om.writeValue(object, sw); 
Map<String, Object> map = om.readValue(sw.toString(), Map.class); 

Và chiến thắng thêm tất nhiên là điều này là đệ quy và sẽ tạo ra bản đồ bản đồ nếu nó cần phải

+1

Đó là _way_ quá vòng xoay khi bạn có thể sử dụng thẳng JavaBeans introspection. –

+1

Bạn không chắc chắn mã nào ít mã hơn. –

+0

Tôi thích cách này bởi vì tôi không phải quan tâm bất cứ điều gì về sự phản chiếu hoặc cách đối tượng thực sự được cấu trúc. –

14

Dưới đây là một xấp xỉ thô, hy vọng đủ để giúp bạn có được chỉ đúng hướng:

public Map<String, Object> getMap(Object o) { 
    Map<String, Object> result = new HashMap<String, Object>(); 
    Field[] declaredFields = o.getClass().getDeclaredFields(); 
    for (Field field : declaredFields) { 
     result.put(field.getName(), field.get(o)); 
    } 
    return result; 
} 
+3

+1 Tôi nghĩ rằng giữa giải pháp của bạn và của tôi, OP nên có nó sussed ra. (Giao dịch của bạn với các lĩnh vực chỉ; tôi đề với phương pháp getter chỉ .--) –

+0

mỏ giao dịch với tất cả mọi thứ ha :) –

+0

Nó có lẽ tốt hơn nếu bạn lặp qua tất cả các lĩnh vực, cả hai tuyên bố và thừa hưởng. – biziclop

44

Sử dụng Apache Commons BeanUtils: http://commons.apache.org/beanutils/.

An thực hiện Bản đồ cho JavaBeans trong đó sử dụng mẫn để có được và đặt thuộc tính trong đậu:

Map<Object, Object> introspected = new org.apache.commons.beanutils.BeanMap(object); 

Lưu ý: mặc dù thực tế API trả về Map<Object, Object> (từ 1.9.0), các lớp học thực tế cho các phím trong bản đồ trở lại là java.lang.String

+3

+1 Ok mã này chính thức là số tiền ít nhất. –

+1

Dường như giải pháp này chỉ là một phương pháp xử lý 'put' cho đến nay :) – Andrey

+0

điều này tạo ra 'Bản đồ ', không phải 'Bản đồ vvondra

35

Một cách khác để sử dụng JacksonObjectMapperconvertValue ví dụ:

ObjectMapper m = new ObjectMapper(); 
Map<String,Object> mappedObject = m.convertValue(myObject,Map.class); 
+0

Câu trả lời này một cách dứt khoát và chuyên nghiệp giải quyết vấn đề ban đầu. Công việc tốt! –

+0

Phụ thuộc gradle cho Android là gì? –

+0

không kiểm tra tất cả các con đường xuống nhưng phải là một cái gì đó như 'biên dịch 'com.fasterxml.jackson: jackson-parent: 2.7-1'' (tất nhiên từ [maven] (http: // search. maven.org/#artifactdetails|com.fasterxml.jackson |jackson-parent|2.7-1|pom)) – Endeios

2

Không có công cụ nào trong các thuộc tính lồng nhau, đối tượng ánh xạ thực hiện một công việc hợp lý ngoại trừ việc bạn phải đặt tất cả các giá trị trên tất cả các trường bạn muốn xem trong bản đồ và thậm chí bạn không thể tránh/bỏ qua các đối tượng chú thích @Json một cách dễ dàng trong ObjectMapper một số thuộc tính. Thật không may, bạn phải làm một cái gì đó như sau, nó chỉ là một dự thảo để chỉ đưa ra một ý tưởng.

/* 
    * returns fields that have getter/setters including nested fields as 
    * field0, objA.field1, objA.objB.field2, ... 
    * to take care of recursive duplicates, 
    * simply use a set<Class> to track which classes 
    * have already been traversed 
    */ 
    public static void getBeanUtilsNestedFields(String prefix, 
      Class clazz, List<String> nestedFieldNames) throws Exception { 
     PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz); 
     for(PropertyDescriptor descr : descriptors){ 
      // if you want values, use: descr.getValue(attributeName) 
      if(descr.getPropertyType().getName().equals("java.lang.Class")){ 
       continue; 
      } 
      // a primitive, a CharSequence(String), Number, Date, URI, URL, Locale, Class, or corresponding array 
      // or add more like UUID or other types 
      if(!BeanUtils.isSimpleProperty(descr.getPropertyType())){ 
       Field collectionfield = clazz.getDeclaredField(descr.getName()); 
       if(collectionfield.getGenericType() instanceof ParameterizedType){ 
        ParameterizedType integerListType = (ParameterizedType) collectionfield.getGenericType(); 
        Class<?> actualClazz = (Class<?>) integerListType.getActualTypeArguments()[0]; 
        getBeanUtilsNestedFields(descr.getName(), actualClazz, nestedFieldNames); 
       } 
       else{ // or a complex custom type to get nested fields 
        getBeanUtilsNestedFields(descr.getName(), descr.getPropertyType(), nestedFieldNames); 
       } 
      } 
      else{ 
       nestedFieldNames.add(prefix.concat(".").concat(descr.getDisplayName())); 
      } 
     } 
    } 
Các vấn đề liên quan