Skip to main content

@BeanType Annotation

The @BeanType annotation is applied to classes to control bean introspection and modeling.

This is the model-side annotation in juneau-commons. It carries the attributes that describe the bean itself — which properties to include or exclude, how to name them, what stop class to use, and so on. Format-side concerns (wire format, type names, dictionary, interceptors, implementation class) live on @Marshalled in juneau-marshall.

This split lets a class participate in the bean model without dragging in any marshalling concerns, and lets the bean-modeling layer live in juneau-commons independent of juneau-marshall.

Migration

Prior to the 10.0 refactor, @BeanType's members were part of @Marshalled. @BeanType (in org.apache.juneau.commons.bean) is now the correct annotation for all bean-modeling attributes listed below. The old @Bean annotation has been removed.

Property Includes: properties / p​

Bean property inclusion and ordering can be controlled using @BeanType(properties|p).

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

p is a shortened synonym for properties:

@BeanType(p="street,city,state")
public class Address { ... }

Property Excludes: excludeProperties / xp​

Bean properties can be excluded using @BeanType(excludeProperties|xp).

// Address class with all properties except city and state.
@BeanType(excludeProperties="city,state")
public class Address { ... }

xp is a shortened synonym for excludeProperties:

@BeanType(xp="city,state")
public class Address { ... }
Java Records

Excluding record components is not supported during parsing. Because records are immutable, all components must be provided to the canonical constructor. Excluded components will be omitted from serialization output, but the parser will be unable to instantiate the record if the excluded component values are missing from the input.

Read-Only Properties: readOnlyProperties / ro​

Specifies one or more properties that are serialized normally but silently ignored during parsing.

// city and state are serialized but not parsed.
@BeanType(readOnlyProperties="city,state")
public class Address { ... }

ro is a shortened synonym for readOnlyProperties.

Write-Only Properties: writeOnlyProperties / wo​

Specifies one or more properties that are parsed normally but silently ignored during serialization.

// city and state are parsed but not serialized.
@BeanType(writeOnlyProperties="city,state")
public class Address { ... }

wo is a shortened synonym for writeOnlyProperties.

Unsorted Properties: unsorted​

Bean properties are sorted alphabetically by default. To opt a specific bean out of this default sorting, use @BeanType(unsorted):

// Opt this bean out of the default alphabetical property ordering.
@BeanType(unsorted=true)
public class MyBean { ... }

Sorting can also be disabled globally for all beans via MarshallingContext.Builder.unsortedProperties():

WriterSerializer serializer = JsonSerializer
.create()
.unsortedProperties() // disable sorting for all beans
.build();

Or for individual classes without modifying the bean class itself:

WriterSerializer serializer = JsonSerializer
.create()
.unsortedProperties(MyBean.class) // disable sorting for MyBean only
.build();

Property Namer: propertyNamer​

The @BeanType(propertyNamer) annotation is used to provide customized naming of properties.

Property namers transform bean property names from standard form to some other form. For example, PropertyNamerDLC converts property names to dashed-lowercase, and these will be used as attribute names in JSON and element names in XML.

// Define a class with dashed-lowercase property names.
@BeanType(propertyNamer=PropertyNamerDLC.class)
public class MyBean { ... }

Interface Class: interfaceClass​

The @BeanType(interfaceClass) annotation limits the properties of a bean to those declared on a specific interface class. When specified, only the list of properties defined on the interface class will be used during serialization. Additional properties on subclasses will be ignored.

// Parent class
@BeanType(interfaceClass=A.class)
public abstract class A {
public String f0 = "f0";
}

// Child class
public class A1 extends A {
public String f1 = "f1";
}

A1 a1 = new A1();
String result = Json5.of(a1);
assertEquals("{f0:'f0'}", result); // Note f1 is not serialized.

Note that this annotation can be used on the parent class so that it filters to all child classes. Or can be set individually on the child classes.

Stop Class: stopClass​

The @BeanType(stopClass) annotation limits which properties are included by halting class hierarchy traversal. It is identical in purpose to the stop class specified by Introspector.getBeanInfo(Class,Class). Any properties in the stop class or in its base classes will be ignored during analysis.

For example, in the following class hierarchy, instances of C3 will include property p3 but not p1 or p2:

public class C1 {
public int getP1();
}

public class C2 extends C1 {
public int getP2();
}

@BeanType(stopClass=C2.class)
public class C3 extends C2 {
public int getP3();
}

Find Fluent Setters: findFluentSetters​

When true, fluent setters are detected as writable bean properties.

@BeanType(findFluentSetters=true)
public class MyBean {
public int getId() { ... }
public MyBean id(int id) { ... } // detected as fluent setter for "id"
}

Fluent setters must be public, non-static, take exactly one parameter, and return the bean itself.

Bean Factory: factory​

Specifies a BeanFactory class to use for instantiating the bean instead of relying on a no-arg constructor or static getInstance() method.

@BeanType(factory=MyBeanFactory.class)
public class MyBean { ... }

Format Side: @Marshalled and @MarshalledConfig​

Attributes that affect wire format — type names, dictionary, interceptors, implementation class, examples, and locale/timezone formats — live on @Marshalled (per-class) and @MarshalledConfig (bulk application via on/onClass on REST classes and methods).

The interceptor attribute on @Marshalled can be used to intercept and modify bean getter and setter calls at the marshalling layer:

// Interceptor that strips out sensitive information on Address beans.
public class AddressInterceptor extends BeanInterceptor<Address> {

@Override
public Object readProperty(Address bean, String name, Object value) {
if ("taxInfo".equals(name))
return "redacted";
return value;
}

@Override
public Object writeProperty(Address bean, String name, Object value) {
if ("taxInfo".equals(name) && "redacted".equals(value))
return TaxInfoUtils.lookup(bean.getStreet(), bean.getCity(), bean.getState());
return value;
}
}

// Register interceptor on bean class.
@Marshalled(interceptor=AddressInterceptor.class)
public class Address {
public String getTaxInfo() {...}
public void setTaxInfo(String value) {...}
}

Java Records​

When using @BeanType with Java records, note the following:

  • @BeanType(properties) can be used to control property order, but all record components should be included. Omitting components will prevent parsing since all components must be provided to the canonical constructor.
  • @BeanType(excludeProperties) will exclude components from serialization output, but excluded components will cause parsing to fail if their values are missing from the input.
  • @BeanType(readOnlyProperties) will cause parsing to fail for the same reason.

If you need to omit components during parsing, use @BeanCtor with a non-canonical constructor that provides defaults for the omitted components.

See Also