39

Is there an easy way to convert properties with dot notation to json

I.E

server.host=foo.bar
server.port=1234

TO

{
 "server": {
    "host": "foo.bar",
    "port": 1234
  }
} 
Roman C
  • 49,761
  • 33
  • 66
  • 176
Vitali Bichov
  • 988
  • 2
  • 12
  • 26
  • http://stackoverflow.com/questions/18507067/converting-java-properties-file-into-json-string – Ostap Maliuvanchuk May 26 '14 at 13:56
  • 1
    Show us what you've tried -- it can be done in about 15 straight-forward statements, without using anything "clever". – Hot Licks May 26 '14 at 14:36
  • 1
    to Robadob: I want to use freemarker to evaluate template files. Now given a template and a json freemarker knows to do the evaluation. The problem is that my 'keys' are stored in java properties file format. – Vitali Bichov May 26 '14 at 14:36
  • to user979349: Same thing I want to create a hierarchical json from dot notated properties – Vitali Bichov May 26 '14 at 14:40
  • to Hot Licks: yes but I can't be the first one to encounter this. The real question is: is there any package (that I could import with maven) that does the above – Vitali Bichov May 26 '14 at 14:41
  • Here is my maven plugin for resolving such problem during build: https://github.com/kirilldev/json-props-generator-plugin – Kirill Reznikov Jan 02 '16 at 18:09

10 Answers10

8

Not the easy way, but I managed to do that using Gson library. The result will be in the jsonBundle String. Here we getting the properties or bundles in this case:

final ResourceBundle bundle = ResourceBundle.getBundle("messages");
final Map<String, String> bundleMap = resourceBundleToMap(bundle);

final Type mapType = new TypeToken<Map<String, String>>(){}.getType();

final String jsonBundle = new GsonBuilder()
        .registerTypeAdapter(mapType, new BundleMapSerializer())
        .create()
        .toJson(bundleMap, mapType);

For this implementation ResourceBundle have to be converted to Map containing String as a key and String as a value.

private static Map<String, String> resourceBundleToMap(final ResourceBundle bundle) {
    final Map<String, String> bundleMap = new HashMap<>();

    for (String key: bundle.keySet()) {
        final String value = bundle.getString(key);

        bundleMap.put(key, value);
    }

    return bundleMap;
}

I had to create custom JSONSerializer using Gson for Map<String, String>:

public class BundleMapSerializer implements JsonSerializer<Map<String, String>> {

    private static final Logger LOGGER = LoggerFactory.getLogger(BundleMapSerializer.class);

    @Override
    public JsonElement serialize(final Map<String, String> bundleMap, final Type typeOfSrc, final JsonSerializationContext context) {
        final JsonObject resultJson =  new JsonObject();

        for (final String key: bundleMap.keySet()) {
            try {
                createFromBundleKey(resultJson, key, bundleMap.get(key));
            } catch (final IOException e) {
                LOGGER.error("Bundle map serialization exception: ", e);
            }
        }

        return resultJson;
    }
}

And here is the main logic of creating JSON:

public static JsonObject createFromBundleKey(final JsonObject resultJson, final String key, final String value) throws IOException {
    if (!key.contains(".")) {
        resultJson.addProperty(key, value);

        return resultJson;
    }

    final String currentKey = firstKey(key);
    if (currentKey != null) {
        final String subRightKey = key.substring(currentKey.length() + 1, key.length());
        final JsonObject childJson = getJsonIfExists(resultJson, currentKey);

        resultJson.add(currentKey, createFromBundleKey(childJson, subRightKey, value));
    }

    return resultJson;
}

    private static String firstKey(final String fullKey) {
        final String[] splittedKey = fullKey.split("\\.");

        return (splittedKey.length != 0) ? splittedKey[0] : fullKey;
    }

    private static JsonObject getJsonIfExists(final JsonObject parent, final String key) {
        if (parent == null) {
            LOGGER.warn("Parent json parameter is null!");
            return null;
        }

        if (parent.get(key) != null && !(parent.get(key) instanceof JsonObject)) {
            throw new IllegalArgumentException("Invalid key \'" + key + "\' for parent: " + parent + "\nKey can not be JSON object and property or array in one time");
        }

        if (parent.getAsJsonObject(key) != null) {
            return parent.getAsJsonObject(key);
        } else {
            return new JsonObject();
        }
   }

In the end, if there were a key person.name.firstname with value John, it will be converted to such JSON:

{
     "person" : {
         "name" : {
             "firstname" : "John"
         }
     }
}

Hope this will help :)

yyunikov
  • 5,719
  • 2
  • 43
  • 78
  • Hi, Yuriy, I used Gjson2.3 and have the exception Exception in thread "main" java.lang.ClassCastException: com.google.gson.JsonPrimitive cannot be cast to com.google.gson.JsonObject at com.google.gson.JsonObject.getAsJsonObject(JsonObject.java:182) at tools.BundleMapSerializer.getJsonIfExists(BundleMapSerializer.java:67) – Rocky Hu Nov 04 '14 at 04:35
  • The problem is that the object in properties can not be the primitive and JSON object at the same time, so you need to avoid key duplicates like: key.name.a=something, key.name=something2 – yyunikov Nov 04 '14 at 08:11
  • yes, you are right, I found my problem, just like you said – Rocky Hu Nov 05 '14 at 13:24
5

Using lightbend config java library (https://github.com/lightbend/config)

String toHierarchicalJsonString(Properties props) {
  com.typesafe.config.Config config = com.typesafe.config.ConfigFactory.parseProperties(props);
  return config.root().render(com.typesafe.config.ConfigRenderOptions.concise());
}
raisercostin
  • 8,777
  • 5
  • 67
  • 76
4

It is pretty easy, download and add to your lib: https://code.google.com/p/google-gson/

Gson gsonObj = new Gson();
String strJson =  gsonObj.toJson(yourObject);
klapvee
  • 139
  • 7
2

You can try with https://github.com/mikolajmitura/java-properties-to-json

You can generate Json from:

  • from Java properties (java.util.Properties)
  • from Map (import java.util.Map) -> Map<String,String>
  • from Map (import java.util.Map) -> Map<String,Object>
  • from InputStream with properties (java.io.InputStream)
  • from given file localization with properties
  • from File with properties (java.io.File)


code example:

import pl.jalokim.propertiestojson.util.PropertiesToJsonConverter;

...

Properties properties = ....;
String jsonFromProperties = new PropertiesToJsonConverter().convertToJson(properties);

InputStream inputStream = ....;
String jsonFromInputStream = new PropertiesToJsonConverter().convertToJson(inputStream);

Map<String,String> mapProperties = ....;
String jsonFromInputProperties = new PropertiesToJsonConverter().convertToJson(mapProperties);

Map<String, Object> valuesAsObjectMap = ....;
String jsonFromProperties2 = new PropertiesToJsonConverter().convertFromValuesAsObjectMap(valuesAsObjectMap);

String jsonFromFilePath = new PropertiesToJsonConverter().convertPropertiesFromFileToJson("/home/user/file.properties");

String jsonFromFile = new PropertiesToJsonConverter().convertPropertiesFromFileToJson(new File("/home/user/file.properties"));

maven dependency:

      <dependency>
          <groupId>pl.jalokim.propertiestojson</groupId>
          <artifactId>java-properties-to-json</artifactId>
          <version>5.1.0</version>
      </dependency>

dependency required minimum java 8.

more example of uses on https://github.com/mikolajmitura/java-properties-to-json

Mikołaj Mitura
  • 121
  • 1
  • 3
  • 3
    You may want to read [How to offer personal open-source libraries?](https://meta.stackexchange.com/q/229085) before posting about your project some more. – Martijn Pieters Aug 04 '16 at 07:07
  • I really need this for java 7 ! – abdelrahman-sinno Feb 14 '17 at 11:42
  • Hi Martijn, just wondering how to keep the original order of properties file elements by using PropertiesToJsonConverter().convertPropertiesFromFileToJson()? This method always returns JSON content in a descending order. Would that be configurable? Many thanks! – xman_bsn Feb 28 '20 at 05:30
  • @xman_bsn this is not configurable, real key properties order is lost by "properties.load(inputStream);" under the hood. but at this moment you can do you implementation of PropertyKeysOrderResolver code below: PropertiesToJsonConverter converter = new PropertiesToJsonConverter(); converter.setPropertyKeysOrderResolver(new PropertyKeysOrderResolver() { public List getKeysInExpectedOrder(Map properties) { // TODO implement way how you want gather properties key order } }); – Mikołaj Mitura Mar 23 '20 at 13:18
  • from 5.1.3 version order of properties in json structure is the same like in the properties file. Converting from Map or Properties class directly order of properties will not be kept. – Mikołaj Mitura May 22 '20 at 06:29
1

I didn't want any dependency on gson and I wanted to return a hierarchical json from a Spring controller so a deep Map was enough for me.

This works for me, just loop over all your keys and pass in an empty map.

void recurseCreateMaps(Map<String, Object> currentMap, String key, String value) {
    if (key.contains(".")) {
        String currentKey = key.split("\\.")[0];

        Map<String, Object> deeperMap;

        if (currentMap.get(currentKey) instanceof Map) {
            deeperMap = (Map<String, Object>) currentMap.get(currentKey);
        } else {
            deeperMap = new HashMap<>();
            currentMap.put(currentKey, deeperMap);
        }

        recurseCreateMaps(deeperMap, key.substring(key.indexOf('.') + 1), value);
    } else {
        currentMap.put(key, value);
    }
}
jgeerts
  • 641
  • 8
  • 17
0

Look at this https://github.com/nzakas/props2js. You can use it manually or fork and use in your project.

Ostap Maliuvanchuk
  • 1,125
  • 2
  • 12
  • 32
0

A little bit recursion and Gson :)

public void run() throws IOException {

    Properties properties = ...;

    Map<String, Object> map = new TreeMap<>();

    for (Object key : properties.keySet()) {
        List<String> keyList = Arrays.asList(((String) key).split("\\."));
        Map<String, Object> valueMap = createTree(keyList, map);
        String value = properties.getProperty((String) key);
        value = StringEscapeUtils.unescapeHtml(value);
        valueMap.put(keyList.get(keyList.size() - 1), value);
    }

    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    String json = gson.toJson(map);

    System.out.println("Ready, converts " + properties.size() + " entries.");
}

@SuppressWarnings("unchecked")
private Map<String, Object> createTree(List<String> keys, Map<String, Object> map) {
    Map<String, Object> valueMap = (Map<String, Object>) map.get(keys.get(0));
    if (valueMap == null) {
        valueMap = new HashMap<String, Object>();
    }
    map.put(keys.get(0), valueMap);
    Map<String, Object> out = valueMap;
    if (keys.size() > 2) {
        out = createTree(keys.subList(1, keys.size()), valueMap);
    }
    return out;
}
Mark
  • 17,887
  • 13
  • 66
  • 93
0

just use org.json.JSONObject constructor that receives a Map (which Properties extends):

JSONObject jsonProps = new JSONObject(properties);
jsonProps.toString();

If you don't already have the properties loaded you can do that from a file

Properties properties= new Properties();
File file = new File("/path/to/test.properties");
FileInputStream fileInput = new FileInputStream(file);
properties.load(fileInput);

If you want to do the reverse, and read a json string into a prop file you can use com.fasterxml.jackson.databind.ObjectMapper:

HashMap<String,String> result = new ObjectMapper().readValue(jsonPropString, HashMap.class);
Properties props = new Properties();
props.putAll(result);
StevenWernerCS
  • 839
  • 9
  • 15
0

Personally I used this dependency:

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-properties</artifactId> 
  <version>2.10.2</version>
  <scope>compile</scope>
</dependency>
private static JavaPropsMapper javaPropsMapper = JavaPropsMapper.builder().build();
final JsonNode request = javaPropsMapper.readPropertiesAs(parameters, JsonNode.class);

Try to use schema validation to avoid possible issues in you mapping.

Although, with a simple object mapper it is possible also

Properties properties = new Properties();
properties.put("array", new int[] {1,2,3});
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValueAsString(parameters);

Output

{"fullname":["Foo","Bar"]}
Harold Castillo
  • 2,006
  • 19
  • 23
-1

Try reading this http://www.oracle.com/technetwork/articles/java/json-1973242.html, you will find several class to work with json.

I guess that retrieving the json from a local file, inner resource in the jar, or in another location specificable by an URL and the just read it with a JsonReader get the job dones.


This is a snipped from the reference site posted before.

 URL url = new URL("https://graph.facebook.com/search?q=java&type=post");
 try (InputStream is = url.openStream();
      JsonReader rdr = Json.createReader(is)) {

      JsonObject obj = rdr.readObject();
      // from this line forward you got what you want :D

     }
 }

Hope it helps!

Victor
  • 3,841
  • 2
  • 37
  • 63