Skip to content
This repository has been archived by the owner on May 28, 2018. It is now read-only.

Improved serialization of java.util.Map #823

Open
glassfishrobot opened this issue Jun 22, 2010 · 9 comments
Open

Improved serialization of java.util.Map #823

glassfishrobot opened this issue Jun 22, 2010 · 9 comments

Comments

@glassfishrobot
Copy link

Currently, to serialize java.util.Map objects to JSON, one needs to write a
special @XmlJavaTypeAdapter annotation and usually covert the map into list of
(key,value) entires, which are then marshalled/unmarshalled by JAXB.
The representation for a key/value pair produced by this approach usually looks
like this:

{"key": "MyKey", "value" : "MyValue"}

This is not very natural for JSON, as

{"MyKey":"MyValue"}

would be a more usual
JSON-like representation. Unfortunately, this JSON-friendly representation
cannot be easily produced by jersey-json, because it works via JAXB and JAXB
cannot produce this out of the box easily, as it relies on XML Schemas and this
representation requires XML tag names that are dynamic, which is impossible.

Therefore, it would be nice to introduce the possibility to serialize Maps into
a natural JSON format.

Please find attached a class called JsonMapAdapter.
It can be used like this to achieve the desired effect:

@XmlRootElement(name="YourClassElementName")
class YourClass {
...

@xmlelement(name="YourMapElementName")
// Here is the trick: Use use special map adapter for maps (see below)
@XmlJavaTypeAdapter(JsonMapAdapter.class)
private Map<String,String> yourMap = new LinkedHashMap<String,String>();

...
}

Please see this mailing list discussion for more information:
http://old.nabble.com/Custom-mapping-for-Maps-in-JAXB-ts28779221r0.html

NOTE: This approach seems to work nicely for Map<String,String>. But if values
are JAXB-annotated classes, it most likely would not work out of the box. The
way to solve this issue still needs to be investigated.

Environment

Operating System: All
Platform: All
URL: http://old.nabble.com/Custom-mapping-for-Maps-in-JAXB-ts28779221r0.html

@glassfishrobot
Copy link
Author

Reported by [email protected]

@glassfishrobot
Copy link
Author

[email protected] said:
Created an attachment (id=131)
XML type adapter implementation for producing JSON friendly format from java.util.Map

@glassfishrobot
Copy link
Author

File: JsonMapAdapter.java
Attached By: [email protected]

@glassfishrobot
Copy link
Author

[email protected] said:
Created an attachment (id=133)
New version of JsonMapAdapter that supports also JAXB-annotated classes as Values of Maps

@glassfishrobot
Copy link
Author

File: JsonMapAdapter.java
Attached By: [email protected]

@glassfishrobot
Copy link
Author

[email protected] said:

NOTE: This approach seems to work nicely for Map<String,String>. But if values
are JAXB-annotated classes, it most likely would not work out of the box. The
way to solve this issue still needs to be investigated.

I attached a new version of the JsonMapAdapter class, which tries to address the
mentioned limitation. It seems to work for me on a bunch of use-cases. In
particular, values inside maps can be @XmlRootElement types. And this new
version also supports @XmlRootElement, where name or namespace annotation
parameters are not specified.

An important point for mapped notation is not to forget to call the xml2JsonNs
in the builder with a complete set of namespace used by your JAXB classes, as
namespace information is otherwise lost in the mapped notation.

Please test if it works for you and provide input about discovered problems.

TODO: Right now, unmarshalling would only work for a value of class C, if at
least one object of class C was marshalled before as a value of the map. So,
first marshal, then unmarshal. The reason for this limitation is due to the fact
that JsonMapAdapter needs to be able to map qualified XML element names to the
JAXB annotated classes and JAXBContexts. And at the moment this mapping is done
implicitly during marshalling.
Alternatively, one can think about explicit APIs for developers to provide such
a mapping at run-time to the generic JsonMapAdapter.

@glassfishrobot
Copy link
Author

mike_meessen said:
romixlev, your solution looks like a good starting point!

I ran into an issue though:
in your "marshal" method, the following line fails if the key is numeric:

Element keyValueElement = doc.createElement(entry.getKey().toString());

entity.getKey().toString() produces e.g. "4", which is not a valid xml entity and causes:

SEVERE: org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: An invalid or illegal XML character is specified.
SEVERE: at com.sun.org.apache.xerces.internal.dom.CoreDocumentImpl.createElement(CoreDocumentImpl.java:618)

Any idea how to fix this? If I change the line to the following, it works but output is obviously wrong:

Element keyValueElement = doc.createElement("x" + entry.getKey().toString());

All in all, XML isn't Json and having to jump through it ends up being a real pain :/

@glassfishrobot
Copy link
Author

eskatos said:
@mike_meessen I ran into the very same issue, it is impossible to use a string key starting with a number. In case of UUIDs it works only when the UUID start with a letter.

From what I understand it's impossible to solve the issue except by removing JAXB from the equation.

That's a real pitty that JAX-RS 1 cannot produce simple json like that. I don't know about JAX-RS 2 but if it's still based on JAXB the problem will remain.

I hope someone can prove us wrong and tell us "there is a way and this is ...".

Regards

@glassfishrobot
Copy link
Author

This issue was imported from java.net JIRA JERSEY-551

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants