Chaitanya Chaitu
Chaitanya Chaitu

Reputation: 1

Serialize a HashMap of key value pairs and convert it into a formatted string in Java

I declared a hashmap of key value pairs as,

HashMap<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");

I am using a method to serialize this hashmap and convert it into a string using the following code:

public static String serializeMetadata(HashMap<String, String> metadata) throws IOException {
    try(ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
        ObjectOutputStream oos = new ObjectOutputStream(baos)) {
        oos.writeObject(metadata);
        return baos.toString();
    }
}

When I call serializeMetadata method and pass my hashmap as input, I expect map state to be streamed by ObjectOutputStream(oos) and store array of bytes in ByteArrayOutputStream(baos). when a toString() is called with baos, I expect an output of

"{key1=value1, key2=value2, key3=value3}"

Just the same kind of output when we convert a hashmap to string. Instead, I get some not readable formatted string like,

���sr�java.util.HashMap���`��F� loadFactorI� thresholdxp?@�����������t�key1t�value1t�key2t�value2t�key3t�value3x

I understand, if I deserialize the baos, I get a hashmap again and if I do a toString(), I get the format I need.

I need a formatted string when serialized hashmap is converted to string, so, 1. I can write a unit test and assert hashmap for a string 2. Persist that string(that has all formatted keys and values) in DB for future data analysis. 3. I can call the string from DB in an application and deserialize it back to a hashmap and continue using the existing states.

I don't want a JSON string. Imagine all key value pairs of hashmap are stored in a single field of DB. I could not find the right solution to it, So if there are any duplicate links, please put them in comments. Any help is appreciated. Thanks!

Upvotes: 0

Views: 2198

Answers (2)

Stephen C
Stephen C

Reputation: 719239

First of all, you say that you don't need a JSON string. But based on what you intend to use this for, a JSON string would work just fine.

But here's a quick and dirty suggestion to serialize the values as a delimiter separated string:

  1. Copy the Map to a TreeMap so that the keys are sorted into a consistent order.

  2. Use values() on the TreeMap to get the values as a list.

  3. Serialize the list as a string; e.g. using StringJoiner and your preferred delimiter.

Problems:

  1. If the key sets for the maps are inconsistent, then your rendering will be ambiguous.

  2. If the toString() method for the value types are not appropriate, you may end up with ambiguous renderings; e.g. if your chosen field delimiter also appears in the toString() output.

  3. If your map's key names change over time, you may run into trouble.

  4. You cannot reconstruct the map without knowing what the key names ought to be.

Using JSON avoids all of those issues: Hint! Using a CSV library would solve 1. and 2., if you could get it to encode one row at a time.


Another alternative is to use ObjectOutputStream to serialize your map to a ByteArrayOutputStream, then encode the captured bytes using base64 encoding. The result can be stored in the database and used to reconstruct the HashMap later.

The disadvantage is that the base64 encoding is opaque, and will probably occupy more space that a JSON version.


You said about your current version:

... I understand, if I deserialize the BAOS, I get a hashmap.

That is true. However, if you "decode" the BAOS contents to a String you would be liable to run into problems, depending on the charset you chose. Your "unreadable string" is a typical example of what happens, and those '�' characters probably indicate characters that could not be decoded properly, and won't re-encode.

Upvotes: 0

Mattias Isegran Bergander
Mattias Isegran Bergander

Reputation: 11909

ObjectOutputStream outputs in a binary format and need an ObjectInputStream to read. If you go via String in between as well it will probably not even work.

The easiest solution is probably to use existing classes such as java.util.Properties which inherits from Hashtable (and implements Map as well) and can take all entries from another Map or act as one. Properties can easily be stored to disk via load/store methods (both line based strings key=value and xml format).

To save to a text readable file:

Properties<String, String> props = new Properties<>();
props.putAll(map);
props.store(...);

To read them back again:

Properties<String, String> props = new Properties<>();
props.load(...);

map.putAll(props);
//or just use the props object as your Map as it implements the Map interface, 
//so instead just do this:
Map<String, String> map = props;

https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html

Upvotes: 1

Related Questions