0

Task: Read a XML file to create a Template;

I am reading this XML file as mentioned below and putting it in a HashMap. to get a Key-Value pair, where Key is value under Element attribute "name" and Value is Element Value. eg: Key: map.abc.color.PRIMARY and Value: #FFFFFF eg: Key: map.abc.node.TEXT1 and Value: value1

<properties>
    <property name="map.abc.color.PRIMARY">#FFFFFF</property>
    <property name="map.abc.color.SECONDARY">#F0F0F0</property>
    <property name="map.abc.node.TEXT1">value1</property>
    <property name="map.abc.node.TEXT2">value2</property>
    <property name="map.abc.node.lowercase">value3</property>   
    <property name="map.abc.strFile">/path/to/file</property>

    <property name="map.pqr.color.PRIMARY">#000000</property>
    <property name="map.pqr.color.SECONDARY">#ABABAB</property>
    <property name="map.pqr.node.WORD1">value4</property>
    <property name="map.pqr.node.WORD2">value5</property>
    <property name="map.abc.node.lowercase">value6</property>
    <property name="map.pqr.strFile">/path/to/file</property>
</properties>

Following is a Template(using a StringBuffer) Output to be written to a file.

abc = {
    color: {PRIMARY_COLOR:"#FFFFFF",SECONDARY_COLOR:"#F0F0F0"}
    node:{TEXT1:"value1",TEXT2:"value2"}
};

pqr = {
    color: {PRIMARY_COLOR:"#FFFFFF",SECONDARY_COLOR:"#F0F0F0"}
    node:{WORD1:"value4",WORD2:"value5"}
};

Offnote: I am using following pattern which works fine.

key.matches("map.abc.*.*\\p{Lu}$") or key.matches("map.*.*\\p{Lu}$")

I am hence looking to find a way to get all keys ending with Uppercase alphabets after the last period in the Key from the HashMap(Or possibly any other options)

2
  • you'll have to iterate through the whole set of keys and check (with regex or simply splitting the string) which ones have the last part in uppercase. Commented Oct 3, 2014 at 1:46
  • You're using the wrong data structure for the job. A database would do this quite easily. Or else you need an auxiliary map. Commented Oct 3, 2014 at 1:47

3 Answers 3

1

You can match the key with the following regex and see whether it returns true:

key.matches(".*\\.[A-Z]+");
Sign up to request clarification or add additional context in comments.

3 Comments

thiw only works if the key ends in uppercase. his statement says any part of the key. though the examples show only ending in uppercase
One the other hand, the last line of the OP says that. I think, when he said last element, he meant the last chunk of his ". separated" string.
Just to clear up, if the key is "map.abc.color.SECONDARY" then I need the key which ends in uppercase after the final period. I was using the following pattern to retrieve it earlier and was working: key.matches("map.abc.*.*\\p{Lu}$") or may be key.matches("map.*.*\\p{Lu}$")
1

OK, now that we have more detail, we can address your data structure. It appears to me that you have a hierarchical map here. Your name seems to define the hierarchy. I'd use a map of maps to store it:

Map<String, Map<String, Map<String, String>>> structure = new HashMap<>();

to parse and populate the map (totally just psuedocode):

path = name.split(".")
// ignore path[0] since it's just 'map'
structure.get(path[1]).get(path[2]).put(path[3], value)

And to retrieve values

for (Map<String, Map<String, String>> element : structure){
  for(Map<String, String> group : element) {
    for(String attribute : group.keys){
      if(allUpper(attribute)) {  //DO YOUR THING HERE }
    }
  }
}

Comments

0

It looks to me like you are trying to convert from a java properties-style list of values to javascript objects.

Here is one possibility: you could "unflatten" the properties-style Map into a recursive map of maps/values, and then leverage one of the JSON libraries to output the values. Here is some code to unflatten things:

public class Foo {
  private static final String ROOT_NAME = "map";

  private static boolean noLowercase(CharSequence s) {
    return !s.chars().anyMatch(Character::isLowerCase);
  }

  private static Map<String, Object> getNextMap(Map<String, Object> map, String mapName) {
    Object nextObj = map.get(mapName);
    if (nextObj == null) {
      nextObj = new LinkedHashMap<String, Object>();
      map.put(mapName, nextObj);
    }
    @SuppressWarnings("unchecked")
    Map<String, Object> nextMap = (Map<String, Object>)nextObj;
    return nextMap;
  }

  public static Map<String, Object> unflatten(Map<String, String> properties) {
    List<String> sortedKeys = new ArrayList<>(properties.keySet());
    Collections.sort(sortedKeys);
    Map<String, Object> rootObject = new LinkedHashMap<>();
    for (String key : sortedKeys) {
      String[] splitKey = key.split("\\.");
      String lastItem = splitKey[splitKey.length - 1];
      if (noLowercase(lastItem)) {
        Map<String, Object> currentMap = rootObject;
        for (int i = 1; i < splitKey.length - 1; ++i) {
          currentMap = getNextMap(currentMap, splitKey[i]);
        }
        currentMap.put(lastItem, properties.get(key));
      }
    }
    return rootObject;
  }
}

Serialising with, for example, Gson or Jackson should be pretty easy after this. Here is an example using Gson:

  public static void printMap(Map<String, String> map) {
    Gson gson = new GsonBuilder().setPrettyPrinting().create();

    for (Map.Entry<String, Object> entry : unflatten(map).entrySet()) {
      System.out.format("%s = ", entry.getKey());
      System.out.print(gson.toJson(entry.getValue()));
      System.out.println(";");
      System.out.println();
    }
  }

Alternatively it should not be too hard to write your own printing routines for the map of maps data structure.

Comments

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.