Skip to main content

Dynamically Applied Annotations

In the section Swaps, you were introduced to annotations that can be applied to bean classes, methods, fields, and constructors such as @BeanType:

// Address class with only street/city/state properties (in that order).
// All other properties are ignored.
@BeanType(properties="street,city,state")
public class Address { ... }

An alternate way of applying these annotations is to attach them to unrelated classes and methods and then tell your serializer or parser where to find them. Each annotation pairs with a sibling *Apply annotation that separates the targeting (on()/onClass()) from the content (value()).

// Unannotated class.
public class Address { ... }

@BeanTypeApply(onClass=Address.class, value=@BeanType(properties="street,city,state"))
public static class DummyClass {}

WriterSerializer serializer = JsonSerializer
.create()
.applyAnnotations(DummyClass.class)
.build();

String json = serializer.write(addressBean);

The advantage to this approach is it allows you to use Juneau annotations on classes/methods/fields/constructors where you might not have access to the source code, or when you only want to selectively apply the annotation under certain scenarios instead of globally.

For example, the following shows the @BeanTypeApply annotation being selectively applied on a single REST method (described later in juneau-rest-server):

@RestGet
@BeanTypeApply(onClass=Address.class, value=@BeanType(properties="street,city,state"))
public List getAddresses() {}

Each *Apply annotation has an on()/onClass() targeting method and applies its value() annotation to the specified targets. The available *Apply annotations are:

BeanTypeApply MarshalledApply BeanCtorApply BeanIgnoreApply MarshalledIgnoreApply BeanPropApply MarshalledPropApply ExampleApply NamePropertyApply ParentPropertyApply SwapApply UriApply BsonApply CborApply CsvApply HjsonApply HoconApply HtmlApply IniApply JsonApply MarkdownApply SchemaApply MsgPackApply OpenApiApply ParquetApply PlainTextApply ProtobufApply PrototextApply SoapXmlApply UonApply UrlEncodingApply XmlApply

The valid pattern matches are:

  • Classes:
    • Fully qualified: com.foo.MyClass
    • Fully qualified inner class: com.foo.MyClass$Inner1$Inner2
    • Simple: MyClass
    • Simple inner: MyClass$Inner1$Inner2, Inner1$Inner2, Inner2
  • Methods:
    • Fully qualified with args: com.foo.MyClass.myMethod(String,int), com.foo.MyClass.myMethod(java.lang.String,int), com.foo.MyClass.myMethod()
    • Fully qualified: com.foo.MyClass.myMethod
    • Simple with args: MyClass.myMethod(String,int), MyClass.myMethod(java.lang.String,int), MyClass.myMethod()
    • Simple: MyClass.myMethod
    • Simple inner class: MyClass$Inner1$Inner2.myMethod, Inner1$Inner2.myMethod, Inner2.myMethod
  • Fields:
    • Fully qualified: com.foo.MyClass.myField
    • Simple: MyClass.myField
    • Simple inner class: MyClass$Inner1$Inner2.myField, Inner1$Inner2.myField, Inner2.myField
  • Constructors:
    • Fully qualified with args: com.foo.MyClass(String,int), com.foo.MyClass(java.lang.String,int), com.foo.MyClass()
    • Simple with args: MyClass(String,int), MyClass(java.lang.String,int), MyClass()
    • Simple inner class: MyClass$Inner1$Inner2(), Inner1$Inner2(), Inner2()
  • A comma-delimited list of anything on this list.