【问题标题】:How to convert Java Enum to Json at runtime?如何在运行时将 Java Enum 转换为 Json?
【发布时间】:2012-10-08 12:59:49
【问题描述】:

我有一个枚举如下:

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;
    }
}

现在我正在使用 google 反射库,并提出了一个自定义注释,我在上面使用@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()));
            }
        }
    }

此代码因以下异常而中断:线程“main”java.lang.IllegalArgumentException 中的异常:参数数量错误

任何帮助将不胜感激。最终目标是能够获得 SomeType{SOME_KEY:"display-value"} 的 json 对象。为此,我无法使用反射获取枚举常量的值。

【问题讨论】:

  • 老实说:我发现可变的enum 是一个非常奇怪的构造,并认为它是一种代码气味。
  • 您能否进一步澄清一下“代码气味”!另外,我不希望枚举是可变的,因为我不打算修改它。我想要的只是动态地生成一个 JSON,这样我就不需要在任何地方对类名进行硬编码。这必须在客户端使用。
  • 为什么不简单地使用“GSON”并让它为您生成 JSON?它具有您正在寻找的所有功能,包括注释和其他东西。
  • @seba.wagner:GSON 将任何现有的 Enum 对象转换为具有封装类中属性名称的字符串,例如对于属性私有 SomeType 类型,GSON 执行 {...,type:"SOME_KEY", ...}。我需要整个 Enum 作为 json,我认为 GSON 不这样做!
  • @JoachimSauer 可变的enum 是一个奇怪的概念,但我不明白这与原始问题有何关系。在我看来,他并没有试图做那样的事情。

标签: java json reflection enums annotations


【解决方案1】:

我之前的回答是错误的。这里发生的是 Enum 类定义了一个 public static valueOf(Class&lt;?&gt;, String) 方法。当 java 编译器将你的enum SomeType 转换为一个类时,它会生成一个public class SomeType extends Enum&lt;SomeType&gt;,并将另一个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});

您的问题应该会得到解决。

【讨论】:

  • 这最终返回了 .values() 的作用,因此我最初没有接受这个!
  • 那你想要什么?这就是您当前的代码的作用?你真的想在枚举成员上调用你的 getLabel() 方法吗?如果是这样,为什么不直接使用 annotated.getMethod("getValue") 呢?
  • 是的,我可以做 annotated.getMethod("getValue");并在迭代 getEnumConstants() 的 for 循环中使用 getValue.invoke(constant) 调用它。但是,这意味着在我打算通用使用的类中对方法名称进行硬编码,并且还强制枚举作者始终将方法命名为完全相同。使用我的方法,枚举的作者可以告诉可以向泛型类传达要使用的实际方法名称的注释。为您的回复 +1 :)
【解决方案2】:

这个呢:

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

【讨论】:

    【解决方案3】:

    所以这就是我最终做的事情: 我修改了@makejson 注释以包含一个名为 label 的字段,该字段是必需的,应该设置为可以返回与枚举中的 SOME_KEY 关联的描述的方法。

    注释如下所示:

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

    所以枚举声明处的注释看起来像 @makejson(label="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
                }
            }
        }
    

    所以最后我通过使用注解在运行时插入此信息,从而规避了无法使用反射调用方法的问题!

    【讨论】:

    • 或者更好的是,您可以在返回标签的方法上使用另一个注释,这样当有人在重构时更改方法的名称而忘记更改值时,您的代码不会中断在注释中。
    • 是的,这实际上更好:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多