HashMap toString, what about the other way?


B

Bas

Hi,

is there a way to reconstruct a HashMap from the output of toString?

E.g.

HashMap m = new HashMap();

// put some stuff in it

String s = m.toString();

// s contains something like {blah=123, lalanana=hutsefluts}

HashMap c = (HashMap)somethingThatICantFind(s);

Or I'm a hoping for something that just doesn't work that way? ;)

cheers,

bas.
 
Ad

Advertisements

L

Lew

Bas said:
Hi,

is there a way to reconstruct a HashMap from the output of toString?

E.g.

HashMap m = new HashMap();

// put some stuff in it

String s = m.toString();

This is already pathological.
// s contains something like {blah=123, lalanana=hutsefluts}

HashMap c = (HashMap)somethingThatICantFind(s);

Or I'm a hoping for something that just doesn't work that way? ;)

You could hack (or read the source code for) the HashMap String representation
and develop a parse algorithm, but it's possible that the HashMap toString()
implementation could change between JVMs or between JVM versions. Unlikely,
but possible.

String seems like an incredibly bad choice for object serialization.
 
P

Patricia Shanahan

Lew said:
This is already pathological.


You could hack (or read the source code for) the HashMap String
representation and develop a parse algorithm, but it's possible that the
HashMap toString() implementation could change between JVMs or between
JVM versions. Unlikely, but possible.

It is not necessary to read the code, just the API documentation.
HashMap inherits toString from AbstractMap, and AbstractMap's
documentation says exactly what it produces.

It would work if, and only if, there is enough additional data to
recreate each key and value from its toString. For example, "42" is the
toString result for both "42" and new Integer(42). For many classes the
toString result is clearly insufficient to recreate the object.

String seems like an incredibly bad choice for object serialization.

Especially since HashMap is Serializable.

Patricia
 
R

Roedy Green

is there a way to reconstruct a HashMap from the output of toString?


// Build a HashMap, convert to String, and recreate the HashMap.
import java.util.HashMap;
import java.util.regex.Pattern;
public class HashString
{

/**
* test harness
*
* @param args not used
*/
public static void main ( String[] args )
{

HashMap<String,String >h1 = new HashMap<String,String>( 11 );
h1.put ("apple","red");
h1.put ("peach","yellow");
h1.put ("mango","orange");
String everything = h1.toString();
System.out.println( everything );
// prints {apple=red, mango=orange, peach=yellow}

// take the answer apart
Pattern p = Pattern.compile("[\\{\\}\\=\\, ]++");
String[] split = p.split( everything );
for ( String s: split )
{
System.out.println( '\"' + s + '\"' );
}
// prints:
// ""
// "apple"
// "red"
// "mango"
// "orange"
// "peach"
// "yellow"

// put it back together again.
HashMap<String,String >h2 = new HashMap<String,String>( 11 );
for ( int i=1; i< split.length; i+=2 )
{
h2.put( split, split[i+1] );
}
System.out.println( h2.toString() );
// prints: {apple=red, peach=yellow, mango=orange}
}
}



Also see http://mindprod.com/jgloss/serialization.html
 
R

raymondpendergraph

Hi,

is there a way to reconstruct a HashMap from the output of toString?

E.g.

HashMap m = new HashMap();

// put some stuff in it

String s = m.toString();

// s contains something like {blah=123, lalanana=hutsefluts}

HashMap c = (HashMap)somethingThatICantFind(s);

Or I'm a hoping for something that just doesn't work that way? ;)

cheers,

bas.

Quick answer is strip the braces from the beginning and end with
methods in String, split that string based on the commas, String.split
each one of those strings by = (name/value pair). Put the trimmed
value on the left as the key and the trimmed value on the right as the
value.

I would use a wrapper to build a cleaner toString based on the
values.
 
D

Daniel Pitts

Bas said:
Hi,

is there a way to reconstruct a HashMap from the output of toString?

E.g.

HashMap m = new HashMap();

// put some stuff in it

String s = m.toString();

// s contains something like {blah=123, lalanana=hutsefluts}

HashMap c = (HashMap)somethingThatICantFind(s);

Or I'm a hoping for something that just doesn't work that way? ;)

cheers,

bas.
The problem with that is that HashMap can contain other things that
aren't reconstructible from a String. Usually, toString should not be
used to produce something that is intended to be parsed. The best use
of toString is to create informative debug information.

So, what is your actual use case? Where does this string come from that
you can't get the original map instead?
 
Ad

Advertisements

B

Bas

I was afraid of this.

I'll figure it out another (i.e. a "better") way. It seemed so obvious
to go from the output of HashMap's toString back to a HashMap. But
it's java, not perl ;)

cheers,

bas.
 
C

Curt Welch

Bas said:
I was afraid of this.

I'll figure it out another (i.e. a "better") way. It seemed so obvious
to go from the output of HashMap's toString back to a HashMap. But
it's java, not perl ;)

cheers,

bas.

Also, the toString() output was never designed to be reversed. You can't
parse it correctly if the strings contain comma, equals, spaces, or braces
in the data:

import java.util.*;

class HashMapTest
{
public static void main(String args[])
{
HashMap<String,String> h = new HashMap<String,String>();

h.put("key1=value1, key2", "value2");
h.put("key3=value3}\n{key4=value4, key5", "value5");

System.out.println(h);

// The hashmap with only two keys in it produces
// this output:
//
// {key1=value1, key2=value2, key3=value3}
// {key4=value4, key5=value5}
//
}
}
 
B

Bas

So, what is your actual use case? Where does this string come from that
you can't get the original map instead?

I'm sending a client-specific (i.e. not controllable by me) value in a
javax.jms.MapMessage. The values in those can only contain primitive
data types and I figured I could take a shortcut when allowing only
simple text-only keys and text-only values in a HashMap.

I know, I'm lazy ;)

I'll put them in the MapMessage directly, with keys starting with
CLIENT_SPECIFIC_XXX where XXX is the client-specific key. And recreate
a HashMap in the jms consumer based on that.

I don't want javax.jms.ObjectMessage (I need language neutrality) and
I don't want XML in a TextMessage either.

Thanks all, it's clear the obvious (well, to me at least) was a little
too obvious ;)
 
L

Lasse Reichstein Nielsen

Roedy Green said:
....
HashMap<String,String >h1 = new HashMap<String,String>( 11 );

If all you have is a Map from String to String, then you might as well
use a Properties object with its more specialized save and load methods.

Even in this very restricted case (String->String), a fromString
method cannot work for all map values
h1.put ("apple","red");
h1.put ("peach","yellow");
h1.put ("mango","orange");

then add:
h1.put("foo = bar, baz", " zip, zap = zup");
and try to retrieve the original values.

/L
 
G

Gordon Beaton

String seems like an incredibly bad choice for object serialization.

On the other hand, it's not fundamentally different than what
java.util.Properties (isa Hashtable) already does with store()/load()
and storeToXML()/loadFromXML().

/gordon

--
 
Ad

Advertisements

L

Lew

Gordon said:
On the other hand, it's not fundamentally different than what
java.util.Properties (isa Hashtable) already does with store()/load()
and storeToXML()/loadFromXML().

Exactly the same, except that Properties assumes everything is a String.
The Properties can be saved to a stream or loaded from a stream.
Each key and its corresponding value in the property list is a string.

String.toString() tends to be readily invertible.

Since the question was asked of a HashMap, one must account for non-invertible
toString() methods from non-String objects.

I suppose you could argue that that is not a /fundamental/ difference, but I'd
beg to differ.

Given certain conventions, one could come up with some sort of Java API for
XML Binding (JAXB) or similar, and use it to map object graphs to an XML
representation.

Hmm, I wonder if that might solve the OP's problem ...
 
S

Stefan Ram

Bas said:
is there a way to reconstruct a HashMap from the output of toString?

I have written an implementation of java.util.Map
to easily do serialization and parsing:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ final RoomSource room = room( "< a=b c=d >" );
System.out.println( room instanceof java.util.Map )
System.out.println( room.get( "a" ));
System.out.println( room.get( "c" ));
System.out.println( room ); }}

true
b
d
< a =b c =d >

The last line (serialization) can be parsed with
»room( java.lang.String )« to get the same room again.

More about it can be found on the following page:

http://www.purl.org/stefan_ram/pub/junotal_tutorial
 
B

bugbear

Bas said:
I was afraid of this.

I'll figure it out another (i.e. a "better") way. It seemed so obvious
to go from the output of HashMap's toString back to a HashMap. But
it's java, not perl ;)

Are you thinking of the (rather remarkable) behaviour
of Data::Dumper() ?

BugBea
 
L

Lasse Reichstein Nielsen

I have written an implementation of java.util.Map
to easily do serialization and parsing: ....
{ final RoomSource room = room( "< a=b c=d >" );

How does it handle map keys or values that contain spaces
or equals? Does it escape them in the toString text?

/L
 
S

Stefan Ram

Lasse Reichstein Nielsen said:
How does it handle map keys

it also can handle keys that are maps itself.

For example,

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ final RoomSource room = room( "< < a=b c=d >=1 >" );
System.out.println( room.get( room( "< a=b c=d >" ))); }}

1

Above, the map "< a=b c=d >" is used as a key and mapped to
the value "1". "room.get" then retrieves the value "1" given
the map "< a=b c=d >" as a key.

The order or details of notation or redundant assertions do
not matter in this case. Strings can also be written in
brackets. Therefore, the notation "< c=d a = c=d >" also
is recognized as the same key, because it is the same map.

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ final RoomSource room = room( "< < a=b c=d >=1 >" );
System.out.println( room.get( room( said:
or values that contain spaces or equals?
Does it escape them in the toString text?

If a string value is to contain an equal sign or a space,
it needs to be written in brackets. The routines will properly
escape when serializing again. For example:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ System.out.println( room( "< [abc=def ghi]=1 >" )); }}

< [abc=def ghi] =1 >

The serialization routine has decided to use brackets here,
because they are required. The space in front of the "=1" is a
minor annoyance (formatting style bug), but it does not change
the semantics just the style of the notation.

If brackets are not needed, they will be omitted:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ System.out.println( room( "< [abcdefghi]=1 >" )); }}

< abcdefghi =1 >

Here, the serialization routine has detected that no
bracket escape is needed.

To see, how any string is escaped, you can insert it,
and then serialize it. The following program adds the
following two strings:

abc[]def[ghi\jkl
mno\]pqr\

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.Room;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ Room room = new Room();
room.add( "abc[]def[ghi\\jkl" );
room.add( "mno\\]pqr\\" );
System.out.println( room ); }}

< [abc[]def\[ghi\jkl] [mno\\]pqr\|] >

Somewhat unusual, but this is specified in this way,
so that brackets and backslashes can be used inside a
bracketed string without escaping as often as possible.
Only unpaired brackets and backslashes at the end of a
string needs special handling.

This output now will be used as input to check, that it
will be serialized again to the same output. Of course,
Java now requires to double backslashes:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.Room;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ Room room = room( "< [abc[]def\\[ghi\\jkl] [mno\\\\]pqr\\|] >" );
System.out.println( room ); }}

< [abc[]def\[ghi\jkl] [mno\\]pqr\|] >
 
Ad

Advertisements

S

Stefan Ram

Supersedes: <[email protected]>


Lasse Reichstein Nielsen said:
How does it handle map keys

it also can handle keys that are maps itself.

For example,

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ final RoomSource room = room( "< < a=b c=d >=1 >" );
System.out.println( room.get( room( "< a=b c=d >" ))); }}

1

Above, the map "< a=b c=d >" is used as a key and mapped to
the value "1". "room.get" then retrieves the value "1" given
the map "< a=b c=d >" as a key.

The order or details of notation or redundant assertions do
not matter in this case. Strings can also be written in
brackets. Therefore, the notation "< c=d a = c=d >" also
is recognized as the same key, because it is the same map.

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ final RoomSource room = room( "< < a=b c=d >=1 >" );
System.out.println( room.get( room( "< c=d a = c=d >" ))); }}

1
or values that contain spaces or equals?
Does it escape them in the toString text?

If a string value is to contain an equal sign or a space,
it needs to be written in brackets. The routines will properly
escape when serializing again. For example:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ System.out.println( room( "< [abc=def ghi]=1 >" )); }}

< [abc=def ghi] =1 >

The serialization routine has decided to use brackets here,
because they are required. The space in front of the "=1" is a
minor annoyance (formatting style bug), but it does not change
the semantics just the style of the notation.

If brackets are not needed, they will be omitted:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.RoomSource;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ System.out.println( room( "< [abcdefghi]=1 >" )); }}

< abcdefghi =1 >

Here, the serialization routine has detected that no
bracket escape is needed.

To see, how any string is escaped, you can insert it,
and then serialize it. The following program adds the
following two strings:

abc[]def[ghi\jkl
mno\]pqr\

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.Room;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ Room room = new Room();
room.add( "abc[]def[ghi\\jkl" );
room.add( "mno\\]pqr\\" );
System.out.println( room ); }}

< [abc[]def\[ghi\jkl] [mno\\]pqr\|] >

Somewhat unusual, but this is specified in this way,
so that brackets and backslashes can be used inside a
bracketed string without escaping as often as possible.
Only unpaired brackets and backslashes at the end of a
string needs special handling.

This output now will be used as input to check, that it
will be serialized again to the same output. Of course,
Java now requires to double backslashes:

import java.lang.String;
import java.lang.System;
import de.dclj.ram.notation.unotal.Room;
import static de.dclj.ram.notation.unotal.RoomFromModule.room;

public final class Main
{ public static void main( final String argv[] )
{ Room room = room( "< [abc[]def\\[ghi\\jkl] [mno\\\\]pqr\\|] >" );
System.out.println( room ); }}

< [abc[]def\[ghi\jkl] [mno\\]pqr\|] >


Supersedes: <[email protected]>
 
Ad

Advertisements

R

Roedy Green

If all you have is a Map from String to String, then you might as well
use a Properties object with its more specialized save and load methods.

There are ways to save and restore your entire HashMap that would be
simpler and more general than parsing that toString mess with a regex.

1. Properties (a Lasse suggested).
2. serialisation as I mentioned earlier.
3. saving one string per line
4. saving as a CSV file. See http://mindprod.com/jgloss/csv.html
5. as DataOutputStream pairs.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top