Skip navigation links

Package js.converter

Convert value types to and from string representation.

See: Description

Package js.converter Description

Convert value types to and from string representation. In this package scope a value type is a class that wrap a single value susceptible to be represented as a single string, e.g. java.io.File or java.net.URL. A value type instance is and instance of a value type. This package is not meant to format/parse objects for/from user interface; converters package is designed for string (de)serialization support usable by string based wire format like JSON, XML or SOAP. This package deals only with value types, not aggregated objects and collections; those structured data format is specific to every wire protocol and is not in scope of this package.

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.

User Defined Converters

Although there are stock converters ready to use, this package is designed to be extensible. In order to create user defined converter one should implement 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.

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.

Self-converting

Self-converting value types are objects that contains both data model and converting logic. To simplify user code, there is no need to declare binding for self-converting value types; they are created on the fly at first attempt to use it. Anyway, there is a catch and user code should be aware of it: binding logic is not executed in the same instance as data model. When bind is registered a different instance is created for conversion logic.
 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();
        }
 }
 

Abstract classes and interfaces

There are cases where a value type desired to be converted is not a concrete class. For example time zone instances returned by JRE are not directly implementing TimeZone interface; there is an internal abstract base class that is private and cannot be bound to a converter instance.

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.

Developer note

As already stated this converter package is designed to be used on string serialization. This process implies some sort of IO operations like file writing/reading or networking communication, operations that are inherently slow. For this reason user defined converters speed is not really critical.
Version:
draft
Author:
Iulian Rotaru
Skip navigation links

Copyright © 2020. All rights reserved.