0

Is there a way to get or set an array element stored in a Java Map?

Example:

If we have a map like this:

{
   name: "Blah",
   friends: ["Foo", "Bar"]
}

Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
myMap.put("name", "Blah");
myMap.put("friends", friends);

Is it possible to use Reflection to get or set the first element in the friends array in the "myMap" from the string: "myMap.friends[0]"

2 Answers 2

2

Your question is not very clearly written and I believe that's why you are not getting the answer you expect but, If I understood your question correctly, you need to parse the following input string at runtime that you don't know beforehand:

myMap.friends[0]

And this should be parsed into components like:

  • mapName = "myMap"
  • mapKey = "friends"
  • valueIndex = 0

And with this information, you need to manipulate data in a Map at runtime through reflection.

Note: This only makes sense if you could potentially have more complex expressions, using different sort of objects and accessing nested properties of retrieved objects, otherwise you wouldn't need reflection at all.

Note 2: You may want to have a look at JXPath which already does a lot of this for you based on a XPath-like syntax for navigating object graphs.

That said, if my assumptions are correct and you still want to do it yourself, consider the following example.

For the sake of demonstration, let's consider our map is returned by a method myMap inside a Context.

private static class Context {
    public Map<String, Object> myMap() {
        Map<String, Object> myMap = new HashMap<>();
        List<String> friends = new ArrayList<>();
        friends.add("Foo");
        friends.add("Bar");
        myMap.put("name", "Blah");
        myMap.put("friends", friends);
        return myMap;
    }
}

I'm assuming you are already parsing the input string into the different components. If not, for this simple string you could do it with simple regular expressions. If you already have the components, let's consider the following method:

public static Object readContextMap(Context context, 
    String mapName, String mapKey, Integer mapValueIndex) throws Exception {

    // gets Context class for inspection
    Class<?> cls = context.getClass();

    // search for a method based on supplied mapName
    Method mapMethod = cls.getDeclaredMethod(mapName);

    // get a value from the retrieved map based on mapKey
    Object mapValue = mapMethod.getReturnType()
            .getDeclaredMethod("get", Object.class)
            .invoke(mapMethod.invoke(context), mapKey);

    // if the result is of type list, use the index to return the indexed element
    if (List.class.isAssignableFrom(mapValue.getClass())) {
        return  ((List<?>)mapValue).get(mapValueIndex);
    }

    // otherwise return the object itself
    return mapValue;
}

For testing purposes, consider the following main method:

public static void main(String[] args) throws Exception {
    Context context = new Context();

    String input = "myMap.friends[0]";
    // parse input into...

    String mapName = "myMap";
    String mapKey = "friends";
    Integer valueIndex = 0;

    Object firstFriend = readContextMap(context, mapName, mapKey, valueIndex);
    System.out.println(firstFriend);
    // prints Foo

    Object name = readContextMap(context, "myMap", "name", null);
    System.out.println(name);
    // prints Blah
}

This should be approximately what you want. You can easily create variations of this to set values as well. Please bear in mind that this code is just for demo purposes and needs a better error handling (e.g. verify if the context is really returning a map and nothing else).

This should be something along the lines you are looking for.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @rph, yes that is what I am looking for.
@F.K. Great. Glad it helped.
2

There's no need to use reflection here. You can simply cast it (which is also unsafe, but less so).

You can just do this:

List<String> friends = (List<String>) myMap.get("friends");
friends.set(0, "Bob");

5 Comments

I need reflection. The application I am working on gets xml mapping files that specify name of the property to get or set, it will be similar to what I wrote above: "myMap.friends[0]" and I need to use reflection to process this string and get/set the nested property. I have no choice. Think Spring BeanUtils but for maps.
@F.K. I'm confused. Is this not a normal Java map with public methods to get and set elements?
We don't know the different keys of the Map beforehand. The map is processed at run time. We read the different keys and values for the map from a xml mapping file. so in the xml mapping file we get the nested value to get or set , in this example it is "myMap.friends[0]". I know it is confusing but not sure how I can explain it more but we do need to use reflection. Thanks for looking into it.
Nothing about what you've described so far needs reflection. Non-Map parts of it might need reflection, but these parts do not.
@F.K. as Louis Wasserman said, there's no need to use reflection. Simply casting it is enough. Reflection would only be necessary if you need to access private fields or instantiate something based on a Class object

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.