问题
Recently, I have been intersted with the JSR-269 annotation processing and I want to write a lib to eliminate some boilerplate code with it, such as json processing. I really generate the code, then, however, I encounter a fatal problem and spent a lot of time but could not slove it.
The problem is, the RoundEnvironment.getElementsAnnotatedWith()
method alway return all the elements annotated with the annotation, I cannot tistinguish which one comes from a specific class. Maybe the problem isn' t clear. I show you the code below.
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.TYPE)
public @interface JsonObject {
}
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface JsonField {
String value() default "";
}
@JsonObject
public class Name {
@JsonField("last") public String last;
@JsonField("first") public String first;
}
@JsonObject
public class User {
@JsonField("age") public int age;
@JsonField("name") public String name;
@JsonField("sex") public boolean sex;
}
the fisrt 2 are the annotation, JsonObject
indicates that the annotated type is an JsonObject and JsonField
indicates that the annotated field is an json field.
the latter 2 are the sample POJO class that I want to gennerate json-parse code.
in the Processor
class, the AbstractProcessor.process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)
method, when I call roundEnv.roundEnv.getElementsAnnotatedWith(JsonField.class)
each type(Name
, User
) in a loop, the return result is all the json field, in the sample above, the result is ["last", "first", "age", "name", "sex"]. In this situatin, I cannot distinguish which field belongs to which POJO.
May be the words cannot explain what I mean. Here is what I do in the process
method.
// the set[Name, User]
Set<? extends Element> jsonObjects = roundEnv.getElementsAnnotatedWith(JsonObject.class);
for (Element jsonObject : jsonObjects) {
// the set[last, first, age, name, sex], **THIS IS THE PROBLEM**
Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(JsonField.class);
// other stuff...
}
Feel free to ask me anything I didn' t mention or not clear. Any suggestion is appreciated, Thanks in advance!
回答1:
You can build a collection of your json object elements by calling the getElementsAnnotatedWith(JsonField.class)
method and filering the result based on the annotation of the enclosing element.
Here is a complete example (using runtime annotation processing for simplicity):
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes("*")
public class ElementFilterProcessor extends AbstractProcessor {
@Retention(RetentionPolicy.RUNTIME)
public static @interface JsonObject {}
@Retention(RetentionPolicy.RUNTIME)
public static @interface JsonField { String value(); }
@JsonObject
public class Name {
@JsonField("last") public String last;
@JsonField("first") public String first;
}
@JsonObject
public class User {
@JsonField("age") public int age;
@JsonField("name") public String name;
@JsonField("sex") public boolean sex;
}
public static void main(String[] args) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final JavaCompiler.CompilationTask task = compiler.getTask(
null,
null,
null,
null,
Collections.singleton(ElementFilterProcessor.class.getName()),
Collections.EMPTY_SET);
task.setProcessors(Collections.singleton(new ElementFilterProcessor()));
task.call();
}
@Override
public boolean process(
final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return true;
}
final Set<? extends Element> jsonFields =
roundEnv.getElementsAnnotatedWith(JsonField.class);
final Map<Element, List<Element>> jsonObjects = new HashMap<>();
for (final Element element : jsonFields) {
final Element classElement = element.getEnclosingElement();
if (classElement.getAnnotation(JsonObject.class) != null) {
List<Element> list = jsonObjects.get(classElement);
if (list == null) {
list = new ArrayList<>();
jsonObjects.put(classElement, list);
}
list.add(element);
}
}
System.out.println(jsonObjects);
return false;
}
}
Output:
{stackoverflow.annotation.ElementFilterProcessor.User=[age, name, sex], stackoverflow.annotation.ElementFilterProcessor.Name=[last, first]}
I'd also recommend to rather consider using a third-party library for mapping Java objects to JSON. For example, the Jackson processor
来源:https://stackoverflow.com/questions/27210759/jsr269-annotation-processing-getelementsannotatedwith-return-all-the-annotated