While parsing into beans, Juneau attempts to determine the class types of bean properties through
reflection on the bean property getter or setter.
Often this is insufficient if the property type is an interface or abstract class that cannot be
instantiated.
This is where bean names and dictionaries come into play.
Bean names and dictionaries are used for identifying class types when they cannot be inferred through
reflection.
Bean classes are given names through the {@link oaj.annotation.Bean#typeName() @Bean(typeName)}
annotation.
These names are then added to the serialized output as virtual "_type" properties (or element
names in XML).
On the parsing side, these type names are resolved to classes through the use of bean dictionaries.
For example, if a bean property is of type Object, then the serializer will add
"_type" attributes so that the class can be determined during parsing.
| @Bean(typeName="foo")
| public class Foo {
| // A bean property where the object types cannot be inferred since it's an Object[].
| @Beanp(dictionary={Bar.class,Baz.class})
| public Object[] x = new Object[]{new Bar(), new Baz()};
| }
|
| @Bean(typeName="bar")
| public class Bar {}
|
| @Bean(typeName="baz")
| public class Baz {}
When serialized as JSON, "_type" attributes would be added when needed to infer the type during
parsing:
| {
| x: [
| {_type:'bar'},
| {_type:'baz'}
| ]
| }
Type names can be represented slightly differently in different languages.
For example, the dictionary name is used as element names when serialized to XML.
This allows the typeName annotation to be used as a shortcut for defining element names for
beans.
When serialized as XML, the bean is rendered as:
| <foo>
| <x>
| <bar/>
| <baz/>
| </x>
| </foo>
Bean dictionaries are registered through the following:
- {@link oaj.annotation.Beanp}
- {@link oaj.annotation.Beanp#dictionary() dictionary}
- {@link oaj.annotation.Bean}
- {@link oaj.annotation.Bean#dictionary() dictionary}
- {@link oaj.BeanContext.Builder}
- {@link oaj.BeanContext.Builder#beanDictionary(Class...) beanDictionary(Class...)}
The bean dictionary setting can consist of any of the following types:
- Any bean class that specifies a value for {@link oaj.annotation.Bean#typeName() @Bean(typeName)}.
- Any subclass of {@link oaj.BeanDictionaryList} containing a collection of bean classes with type name annotations.
- Any subclass of {@link oaj.BeanDictionaryMap} containing a mapping of type names to classes without type name annotations.
- Any array or collection of the objects above.
| // Create a parser and tell it which classes to try to resolve.
| ReaderParser parser = JsonParser
| .create()
| .dictionary(Foo.class, Bar.class)
| .build();
|
| // Use the predefined HTML5 bean dictionary which is a BeanDictionaryList.
| ReaderParser parser = HtmlParser
| .create()
| .dictionary(HtmlBeanDictionary.class)
| .build();
The "_type" property name can be overridden through the following:
- {@link oaj.annotation.Bean}
- {@link oaj.annotation.Bean#typePropertyName() typePropertyName}
- {@link oaj.BeanContext.Builder}
- {@link oaj.BeanContext.Builder#typePropertyName(String) typePropertyName(String)}
When using the annotation, you'll typically want to define it on an interface class so that it can
be inherited by all subclasses.
| @Bean(typePropertyName="mytype", dictionary={MyClass1.class,MyClass2.class})
| public interface MyInterface {...}
|
| @Bean(typeName="C1")
| public class MyClass1 implements MyInterface {...}
|
| @Bean(typeName="C2")
| public class MyClass2 implements MyInterface {...}
|
| MyInterface[] x = new MyInterface[]{ new MyClass1(), new MyClass2() };
|
| // Produces "[{mytype:'C1',...},{mytype:'C2',...}]"
| String json = JsonSerializer.DEFAULT.serialize(x);
Type names do not need to be universally unique.
However, they must be unique within a dictionary.
The following reserved words cannot be used as type names:
object, array, number, boolean, null.
Serialized type names are DISABLED by default.
They must be enabled on the serializer using the
{@link oaj.serializer.Serializer.Builder#addBeanTypes()}
setting.