2012-10-08 20 views
3

我有一個枚舉如下:如何在運行時將Java Enum轉換爲Json?

public enum SomeType { 
    SOME_KEY (some-display-value-as-label); 
    private String label; 

    private SomeType(String label){ 
     this.label=label; 
    } 
    public String getLabel() { 
     return label; 
    } 
    public void setLabel(String value) { 
     this.label = value; 
    } 
} 

現在我使用谷歌的反射庫,並想出了一個自定義的註解,我上面的標記與像@makejson註釋枚舉。

的想法是應用程序啓動時使用掃描反射用於與@makejson註釋所有類,然後生成用於每個這些枚舉的JSON對象。

我試圖做的是在啓動類:

Reflections reflections = new Reflections("my.package.name"); 
Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(MakeJson.class); 
    for (Class<?> annotated : annotatedClasses) { 
     if (annotated.isEnum()) { 
      MakeJson j = annotated.getAnnotation(MakeJson.class); 
      Object[] constants = annotated.getEnumConstants(); 
      Method[] methods = annotated.getMethods(); 
      Method getValue = null; 
      for (Method m : methods) { 
       if ("valueOf".equals(m.getName())) { 
        getValue = m; //get Reference of valueOf method 
        break; 
       }  
      } 
      //List<Object> labels = Arrays.asList(m.invokem.getReturnType().isEnum()(annotated)); 
      for (Object constant : constants) { 
       System.out.println(constant.toString()); 
       System.out.println(getValue.invoke(annotated,constant.toString())); 
      } 
     } 
    } 

此代碼打破了以下異常:異常在線程「主」 java.lang.IllegalArgumentException異常:錯誤的參數個數

任何幫助將不勝感激。最終目標是能夠爲SomeType {SOME_KEY:「display-value」}獲取json對象。爲此,我無法使用反射來獲取枚舉常量的值。

+4

說實話:我發現一個可變'enum'是一個非常** **奇怪的構造,並會認爲這是一個代碼氣味。 –

+0

您能否更好地澄清一下'代碼味道'!另外,我不想讓枚舉變成可變的,因爲我不會修改它。我想要的只是動態生成一個JSON,以便我不需要在任何地方對類名進行硬編碼。這必須在客戶端使用。 – DeeTee

+0

爲什麼不簡單地使用「GSON」並讓它爲你生成JSON?它具有所需的所有功能,包括註釋和內容。 –

回答

0

這就是我最終做的: 我修改了@makejson註釋以包含一個名爲label的字段,該字段是必需的,並且應將其設置爲可返回與枚舉中的SOME_KEY關聯的描述的方法。

這裏的註解是什麼樣子:

@Target({ElementType.TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface MakeJson { 
    String label(); 
} 

所以在枚舉聲明註釋看起來像 @makejson(標籤= 「getLabel」) 公共枚舉SOMETYPE { ... //一樣在上述 問題}

然後在解析類,我只要從註釋的方法,這種方法並調用它的正確的常量:

Reflections reflections = new Reflections("my.package.name"); 
    Set<Class<?>> annotatedClasses = reflections.getTypesAnnotatedWith(MakeJson.class); 
    for (Class<?> annotated : annotatedClasses) { 
     if (annotated.isEnum()) { 
      Jsonify j = annotated.getAnnotation(MakeJson.class); 
      Object[] constants = annotated.getEnumConstants(); 
      Method m = annotated.getMethod(j.label()); // get the method name 
      for (Object constant : constants) { 
       System.out.println(constant.toString()); 
       System.out.println(m.invoke(constant)); 
       // construct json object here 
      } 
     } 
    } 

所以在最後我避開不能夠通過使用註釋堵塞在運行時,這個信息調用使用反射方法的問題!

+1

或者更好的是,您可以在返回標籤的方法上使用另一個註釋,這樣當有人在進行重構時更改方法的名稱時,您的代碼不會中斷,而忘記更改值在註釋中。 – LordOfThePigs

+0

是的,這實際上更好:) – DeeTee

2

我以前的答案是錯誤的。這裏發生的是Enum類定義了public static valueOf(Class<?>, String)方法。當Java編譯器將您enum SomeType成一類,它會生成一個public class SomeType extends Enum<SomeType>,並添加另一個valueOf(String)方法您SomeType類。因此,你最終會在你的課程中使用兩個稱爲「valueOf」的方法。對我來說,似乎你實際上是調用valueOf(Class, String),但真正用於調用valueOf(String)

爲了解決這個問題,從改變你的循環:

Method[] methods = annotated.getMethods(); 
Method getValue = null; 
for (Method m : methods) { 
    if ("valueOf".equals(m.getName())) { 
     getValue = m; //get Reference of valueOf method 
     break; 
    }  
} 

Method getValue = annotated.getMethod("valueOf", new Class<?>[]{String.class}); 

你的問題應該再被修復。

+0

這最終會返回完全是.values(),因此我原本沒有采用這個! – DeeTee

+0

那你想要什麼?這是你現在的代碼做的嗎?你真的想在枚舉成員上調用你的getLabel()方法嗎?如果是這樣的話,爲什麼不使用annotated.getMethod(「getValue」)呢? – LordOfThePigs

+0

是的,我可以做annotated.getMethod(「getValue」);並在迭代getEnumConstants()的for循環中使用getValue.invoke(constant)來調用它。但是,這意味着硬編碼我打算一般使用的類中的方法名稱,並強制枚舉作者始終將該方法命名爲完全相同。使用我的方法,enum的作者可以告訴可以向泛型類傳達關於要使用的實際方法名稱的註釋。 + 1您的回覆:) – DeeTee

1

這個怎麼樣:

Map tmp = new HashMap<SomeType, String>(); 
    for(SomeType type : SomeType.values()){ 
     tmp.put(type, type.getLabel()); 
    } 
    String desiredJson = new Gson().toJson(tmp);