Interface | Description |
---|---|
Converter |
Convert value type instances to and from strings representation.
|
ConverterProvider |
Converter provider is used by converter registry to register converters declared by third party libraries.
|
Class | Description |
---|---|
BooleansConverter |
Boolean values converter.
|
CharactersConverter |
Character values converter.
|
CharsetConverter | |
ClassConverter |
Java class converter.
|
ConverterRegistry |
Converter registry global per JVM.
|
DatesConverter |
Date/time values conversion to/from ISO8601.
|
EnumsConverter |
Enumeration values converter.
|
FileConverter |
File converter.
|
LocaleConverter |
Locale converter for ISO-639 language and ISO-3166 country code format.
|
NumbersConverter |
Numerical values converter.
|
TimeZoneConverter |
Time zone converter.
|
UrlConverter |
URL converter.
|
Exception | Description |
---|---|
ConverterException |
Generic converters exception thrown when a converter implementation fails to do its job.
|
Using converter is trivial: get converter instance from registry and call conversion methods, like
in sample code below. User code should be prepared to deal with ConverterException
thrown if requested value type has no converter registered or conversion fail due to value type string
representation not well formed. This package does not deal with parsing exceptions, they are propagated
to user code after converting parsing exceptions into ConverterException
.
Converter converter = ConverterRegistry.getConverter(); String string = converter.asString(object); ... Object object = converter.asObject(string, Object.class);
To facilitate integration ConverterRegistry
is singleton global per JVM. This is
necessary also to ensure consistency of (de)serialization process. Anyway, it is developer responsibility
to ensure consistency across different Java virtual machines.
Since registry is global and since it stores mappings of value types converters it acts as a global state. This is a serious drawback that could lead to hard to catch bugs dues to converters overriding. For example, a class may need a particular wire format for a value type, different from rest of application, and decide to register a converter for that. This particular converter will become global and used on entire JVM.
Developer is encouraged to register all converters in a single place in order to have a clear picture and avoid class specific converters.
Converter
and register it
to using ConverterRegistry.registerConverter(Class, Class)
, see sample code below.
For completeness here is a list of stock converters supplied by library. Boxed types apply also to
related primitives.
Boolean
Character
Byte
Short
Integer
Long
Float
Double
Enum
Date
Date
Time
Timestamp
Class
File
URL
Locale
TimeZone
Here a is sample code of an user defined converter for a hypothetical MessageID value object. Note
that in the context Converter.asObject(String, Class)
is executed string
argument is already tested for null value; the same is true for object
argument from
Converter.asString(Object)
.
public final class MessageIDConverter implements Converter { @Override public <T> T asObject(String string, Class<T> valueType) { if (string.isEmpty()) { return null; } return (T) new MessageID(string); } @Override public String asString(Object object) { return ((MessageID) object).getValue(); } } ... ConverterRegistry.getInstance().registerConverter(MessageID.class, MessageIDConverter.class);
Warning: Since converter registry is singleton all registered user defined converters are global per virtual machine and have impact on all serialization processes that uses converters. Overriding a built-in converter may have unexpected results, especially in a complex context with many connected modules running in the same virtual machine.
public class PhoneNumber implements Converter { private String value; public String getValue() { return value; } // converter implementation cannot access 'value' state // because conversion is executed in different instance from model instance @Override public <T> T asObject(String string, Class<T> valueType) { return (T)new PhoneNumber(string); } @Override public String asString(Object object) { return ((PhoneNumber)object).getValue(); } }
For such cases converter package uses abstract converters map. Binding is declared as usual, see above description. Internally, binding logic detects it has to do with an interface or abstract class and store binding to an abstract converters map.
When a request for converter instance is made, lookup an entry into abstract converters map that is super-class for requested value type, that is, requested value type is a kind of a class from abstract converters map; then uses that converter instance to create a concrete bound on the fly.
Copyright © 2020. All rights reserved.