Bean Names and Dictionaries

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[]. @BeanProperty(beanDictionary={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.BeanProperty#beanDictionary() BeanProperty(beanDictionary)} - On individual bean properties through the annotation.
- {@link oaj.annotation.Bean#beanDictionary() Bean(beanDictionary)} - On all properties on a bean and all subclasses.
- {@link oaj.BeanContext#BEAN_beanDictionary} - Configuration property on serializers and parsers.
- {@link oaj.BeanContextBuilder#beanDictionary(Object...)} - Builder method on serializers and parsers.

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 p = JsonParser .create() .beanDictionary(Foo.class, Bar.class) .build(); // Same, but use property. ReaderParser p = JsonParser .create() .addTo(BEAN_beanDictionary, Foo.class) .addTo(BEAN_beanDictionary, Bar.class) .build(); // Use the predefined HTML5 bean dictionary which is a BeanDictionaryList. ReaderParser p = HtmlParser .create() .beanDictionary(HtmlBeanDictionary.class) .build();

The "_type" property name can be overridden through the following:

- {@link oaj.annotation.Bean#typePropertyName() Bean(typePropertyName)} - On individual beans through the annotation.
- {@link oaj.BeanContext#BEAN_beanTypePropertyName} - Configuration property on serializers and parsers.
- {@link oaj.BeanContextBuilder#beanTypePropertyName(String)} - Builder method on serializers and parsers.

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", beanDictionary={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 = SimpleJsonSerializer.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#SERIALIZER_addBeanTypes} configuration property.