Showing posts with label Collections. Show all posts
Showing posts with label Collections. Show all posts

Friday, December 02, 2011

Java Tip of the Day: Generic JAXB Map<K, V> XmlAdapter

There are a couple of well-known issues with using Map<K,V> with JAXB. First, JAXB does not like interfaces. Second, it considers a Map<K,V> to be a bean so it does not handle the internal implementation. The JAXB JIRA Issue is here JAXB-223

There are a number of articles published about how to adapt a Map<K,V> to work in JAXB. However, all of them I have seen are very specific mappings like Map<String,String>, or Map<Integer,String>, etc. None of them address the more specific generic case Map<K,V>. This example handles the more general case using generics. All the other examples I looked at online generally follow the example code from the XmlAdapter javadoc. The code snippet does not encapsulate the values, and generally does not adhere to good coding practices.

The process is to create an adapter is as follows:
  1. Create an mapping entry for Map<K,V>. In our case, we will create one called: MapEntryType<K, V> which will map our generic types.
  2. Next we create a value holder for our Map<K,V> which contains a List<MapEntryType<K, V>> of our entries. The value holder looks like MapType<K, V>
  3. Finally, we create an adapter which extends XmlAdapter. In our case, it is very long since I am using Java 6. In Java 7, we can use the diamond <> operator to shorten it. The adapter is XmlGenericMapAdapter<K, V> extends XmlAdapter<MapType<K, V>, Map<K, V>>.
Note: Please note that the MapEntryType<K, V> has the @XmlAccessorType(XmlAccessType.PROPERTY) set to property and the getters are annotated as @XmlElement.

The NetBeans 7.1 RC1 Apache Maven project can be downloaded here: generic-jaxb-map-adapter.zip

MapEntryType<K, V>



MapType<K, V>



XmlGenericMapAdapter<K, V> extends XmlAdapter<MapType<K, V>, Map<K, V>>



Here is the output from our adapter.

We take advantage of JAXB to provide automatic XML Schema definitions for our keys and values. The only problem with this approach is that it is very "chatty". It provides a schema definition for each key and value.

If you know that you are going to be using simple types, it is more appropriate to use a specific type rather than using generics. We would replace all of the <K, V> with <String, String>. This will remove all of the schema definitions and produce an output which is much cleaner. The String adapters are included in the source code for further review.

Coding Tip of the Day: Using Map<?,?> Collections

I was working on some code in the last few days where I came across a protected Map map instance variable. I examined the code and javadocs which indicated that the intent was to provide a mapping of keys and values. However the actual implementation was setting the keys and values to the same value. This again is not really an issue. However, the usage of the Map was incorrect.

See if you can determine the issue from the code below.
The MapValueHolder<K,V> looks like the following. Here is the implementation.
As a general rule, you should use the key to fetch the value. Even if you expect that the values are the same. Since the instance variable is protected it can be modified in a sub-class, or any class in the same package. Since we code to interfaces, or abstractions (super classes). We can not be sure that the implementation class we are provided does what we expect. In this case, it does what we expect.
MapValueHolder Key --> A
MapValueHolder Key --> B
MapValueHolder Key --> C
A --> A
B --> B
C --> C

Here we continue using another implementation.


This time the result looks different.


MapValueHolder Key --> A
MapValueHolder Key --> B
MapValueHolder Key --> C
A --> null
B --> null
C --> null

This is because our MapValueHolder<K,V> is different.


Admittedly, the implementation above is a bit contrived. We really are getting is a Set<K>. In the next example, we get another result.


The result is considerably different.


MapValueHolder Key --> A
A --> A

Here is the implementation. Again, this is a contrived result where we are just returning the first key and value in a Map<K,V>. Here is another example which demonstrates unexpected results. This results in this very strange result.

Map --> {}

Here I use reflection to modify the expected result. If you have followed me to this point, you will have learned a little bit about the Java Type system, reflection, abstraction, and Collections. Yet, you may be asking what is the tip? Here are some rules:
  1. Use the keys to fetch values.
  2. Check for null values. In my examples, I did not check for null values.
  3. Use the correct Java Collection.
The Java Collection Framework covers most common cases. Take the time to read about them. The code above was based on a couple of issues I discovered: (1) Using keys for values, and (2) not using the correct collection. In this case, they should have used a List<V>. If they wanted to use an ordered Map<K,V>, they should have used a LinkedHashmap<K,V>

The code for the examples are here: bad-mapping.implementation.zip

The code was developed using NetBeans 7.1 RC1.

Thursday, August 25, 2011

Type Safe Collection Conversion

I was reading a great article by a friend of mine Checking an Unchecked Cast in Java 5 or Later where he refers to some of the rules from Josh Bloch about making type safe conversions. It was interesting. Glen also provides a good code example of how he does a type safe conversion. I thought I would provide a couple of examples using a List converter. I have heard that a number of folks do not like returning <T> from a method which has a collection associated with it. However like any tool, it is up to the person using it not to hurt themselves (i.e. a screwdriver is not a hammer). In this example I have a couple of methods which do type safe conversion of a List of something. We need to know in advance what we expect that something to be. This is just one example of how you can do a conversion on a collection. I would like to note that there are methods on the Collections class which returns a checked collection. This ensures that any object added to a collection is of particular type. As the API notes, this is for the addition of classes after the collection has been turned into a checked collection. It does not check the Collection for previously added objects. The code below checks all of the object types before doing the conversion, and fails if it encounters the wrong type.

Popular Posts