views:

50

answers:

2

I am looking for the simplest way to do a deep copy of a flat Map<String, String> of nested properties to a bean. Some of the nested properties are interfaces for which I would like to provide a strategy for instantiation. For example:

Map<String, String> customer = new Map<String, String>();
customers.put("id", "123");
customers.put("address.line1", "221B Baker St.");

public class Customer {
  private int id;
  private Address address; //address is an interface
  ... getters/setters ...
}

Note that I do not want to supply explicit mappings, just a strategy for providing a concrete instance for the interface. I assumed that commons-beanutils to do this, but their is a open JIRA ticket for the functionality. A library that has similar functionality is Google GSON which provides an interface called InstanceCreator.

I've implemented a solution on top of beanutils, but is there a bean conversion library that has this functionality built in?

A: 

You're kindof expressing two different things. the easier thing to do would be what you seem to be initially describing , deeply nested maps of strings. So

put("id", "1")  -> map.put("id", "1")
put("address.line1", "221b")  -> ((Map)map.get("address")).put("221B")

(I'm ignoring for brevity the actual safety checks for is the map there, intialize if so, yadda yadda, all they things that make us love java's concise way of doing things.)

and put "a.b.c.d..." just recursively drills down splitting by "."

If you actually want the "address." (since you refer to an Address class in your example) to refer to an object of type Address, that's a bit more complicated, you're going to need something that recognizes the prefix as a class, preferably something custom that responds to a map interface, can be easily instantiated as you traverse the tree, and knows how to create objects of itself from input strings.

I'm not sure about the details of beanutils implementation of the first is, it's not too difficult to write. But a nested map of strings is easy. You could easily pack this up with serialization (since hashmaps and strings serialize) or with a json library, that should take a nested string map with no problems at all (google gson works well).

Steve B.
I modified the question a bit based on your feedback. I'm only trying to deal with a flat map of nested properties. Also, I looked at Google GSON and it has the concept of InstanceCreator which is what I am looking for -- except I am going map -> bean instead of map -> json.
Eric Hauser
A: 

I think you may be making this more difficult than it could be. What I would do is serialize to and from markup or some other format. I'm not sure what your requirements are, but JAXB, XStream, etc. all of excellent marshallers to do just this.

With JAXB you could do something like:

@XmlRootElement(name="customer")
public class Customer
{
    private int id;
    private Address address;
}

@XmlRootElement(name="address")
public class Address
{
    private String city;
}

And with the following XML:

<customer>
    <id>1234</id>
    <address>
        <city>Kansas City</city>
    </address>
</customer>

...could be marshalled and unmarshalled to and fro. Also, there are several other implementations of the JAXB marshaller (such as jersey-json), so you could also marshall in other formats:

{
    "customer": 
    {
        "id":1234,
        "address": 
        {
             "city":"Kansas City"
        }
    }
}

Is there a particular reason why you need to use a Map? If not, you're only resolution is some complicated Reflections utility, and hopefully something like the GSON library can facilitate the complicatedness.

Edit: Just saw your blurb about not wanting mappings...I don't really understand that. After using serialization, any other method of doing something similar seems not worthwhile.

Droo
I'm working on a demo where I already have a number of beans that I have to copy properties onto. I already have the data as a flat indexed map; it is coming out of a document store. I can certainly use JSON, but seeing how I already have the data in the other format, I want to just use the data I had at hand. When I write this for production purposes, I'll probably use some other method entirely for mapping the data.
Eric Hauser