I'm trying to replace a custom JSON (de)serialization in a groovy/grails project with Jackson.
I'm having trouble getting Jackson to output a pretty-printed JSON with keys sorted in a simple 'natural' alphabetic order.
I've tried this (and many variations):
mymap = [ ... ] // Some groovy map
def mapper = new ObjectMapper()
mapper.configure(SerializationConfig.Feature.SORT_PROPERTIES_ALPHABETICALLY, true)
def jsonstring = mapper.defaultPrettyPrintingWriter().writeValueAsString(mymap)
But Jackson stubbornly generates a JSON where the keys seem to be in a random order.
I've tried changing the type of 'mymap' with a TreeMap, and in that case all keys are properly sorted as expected.
I'm wondering if there is a way to get the keys sorted without changing 'mymap' above to a TreeMap (and recursively all of its map values...).
SORT_PROPERTIES_ALPHABETICALLY seems to be intended to do precisely that, but it's not doing it for some reason.
Would you know why that is? Anything I'm doing wrong above?
I've tried with Jackson 1.8.3, 1.8.8 and 1.9.5, same result (random keys).
As stated by #tim_yates, this doesn't work for map keys.
You could use
mapper.configure(SerializationConfig.Feature.ORDER_MAP_ENTRIES_BY_KEYS, true)
With newer version ( >= 2.6.1) the API changed to:
mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);
The documentation for SORT_PROPERTIES_ALPHABETICALLY explicitly says:
Feature that defines default property serialization order used for POJO fields (note: does not apply to Map serialization!)
So I guess you will need to change your input Map (as you say)
As pointed out, this feature just works for POJOs. However, I think there is a feature request to do the same for Maps, at Jackson Jira; and if not, this sounds like a good addition.
But in the meantime I would second #tim_yates suggestion to use intermediate TreeMap for sorting, serializing that: ordering that Map has will be used as is, so this should work.
Related
I am using jackson-dataformat-csv.
I have a pojo which contains a java.util.Map. I know jackson does not support writing a map to csv directly, but is there any workaround? Basically i have semi-random key-value pairs which i need to write to csv. Can i do it manually somehow?
I don't think so.
I've also looked and can't find anything. It's too bad, too, since it would be trivial to do the equivalent of flattening out the map and bringing it up to the top level.
Optionally, it could prefix the name of the map (specified by #JsonProperty) to prevent heading name collisions.
So a class with these members:
#JsonProperty("Name") String firstColumn = "Bart";
#JsonProperty("FamilyMember") Map family = ImmutableMap.of(
"Dad", "Homer",
"Mom", "Marge");
could output like:
Name, FamilyMember.Dad, FamilyMember.Mom
Bart, Homer, Marge
Someone (#taivo) should contribute that!
But if you don't have time, you are probably better off just writing your own mapper to flatten the map into a pojo..
I am trying to serialize some Clojure data structure into a persistent database, and I currently use Chesire for that purpose.
Let's say I have a map that contains namespaced keywords like the following :
{:cemerick.friend/identity {:current friend, :authentications {friend {:identity friend, :roles #{:clojure-cms.handler/user}}}}}
It gets serialized into JSON, like that :
{"cemerick.friend/identity":{"current":"friend","authentications":{"friend":{"identity":"friend","roles":["clojure-cms.handler/user"]}}}}
When reading it back and serializing (with keywordization (parse-string data true)), I get back the following :
{:cemerick.friend/identity {:current friend, :authentications {:friend {:identity friend, :roles [clojure-cms.handler/user]}}}}
How can I parse this JSON with and get the same data as the original ?
Note : this question gives some context to what I am trying to achieve.
Looking at the tests in Chesire, it's quite obvious that the optional keyword parameter to parse-string will affect all name attributes in a JSON object, value attributes like the namespaced keyword in your example are not affected. Actually, your problem is two-fold: the original set is also not converted back correctly.
For the set problem, what you could do is to write a custom decoder as described in the Chesire documentation.
For the original problem, there is probably no direct way other than to post-process the returned map, find the value to :roles and turn the value into a keyword, like so (untested):
(defn postprocess-json [authmap]
(update-in authmap [:authentications :friend :roles] keyword))
I'm trying to parse a grails parameter map to a Json String, and then back to a parameter map. (For saving html form entries with constraint-violations)
Everything is fine as long as there is no hasMany relationship in the parameter-map.
I'm using
fc.parameter = params as JSON
to save the params as JSON String.
Later I'm trying to rebuild the parameter map and create a new Domain-Object with it:
new Foo(JSON.parse(fc.parameter))
Everything is fine using only 1:1 relationships (states).
[states:2, listSize:50, name:TestFilter]
But when I try to rebuild a params-map with multi-select values (states)
[states:[1,2], listSize:50, name:TestFilter]
I'm getting this IllegalStateException:
Failed to convert property value of type org.codehaus.groovy.grails.web.json.JSONArray to required type java.util.Set for property states; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [de.gotosec.approve.State] for property states[0]: no matching editors or conversion strategy found
I tried to use this, but without success:
JSON.use("deep") {
new Foo(JSON.parse(fc.parameter))
}
You can use JsonSlurper instead of the converters.JSON of grails, it maps JSON objects to Groovy Maps. I think this link also might help you.
Edit: Now, if the problem is binding the params map to your domain, you should try using bindData() method, like:
bindData(foo, params)
Note that this straightforward use is only if you're calling bindData inside a controller.
What seems to be happening in your case is that Grails is trying to bind a concrete type of List (ArrayList in the case of JsonSlurper and JSONArray in the case of converters.JSON) into a Set of properties (which is the default data structure for one-to-many associations). I would have to take a look at your code to confirm that. But, as you did substitute states: [1,2] for a method of your app, try another test to confirm this hypothesis. Change:
states:[1,2]
for
states:[1,2] as Set
If this is really the problem and not even bindData() works, take a look at this for a harder way to make it work using object marshalling and converters.JSON. I don't know if it's practical for you to use it in your project, but it sure works nicely ;)
Let's say I have a JSONObject in GWT that looks like this: {"name1":value1, "name2":value2}. Is there a way to remove the "name2":value2 key/value pair and change this object to {"name1":value1}? I have not found any methods that help with this approach in the GWT Javadoc.
I know there are workarounds to this, of course. Since my JSONObject is small, I am currently making a new one and putting in it all the key/value pairs other than the one I want to remove. But this won't work when I plan to pass in the JSONObject to a child function; since only the JSONObject's reference is passed in Java, I need a mutator function to actively change what the method parameter's JSONObject points to. In the worse case, I could convert the JSONObject to a String and regexp out what I don't want. But this seems prone to error and ugly. Any suggestions?
Actually, put()ing a null (as opposed to a JSONNull) value will delete the value for the given key.
I am doing some processing with Groovy/Grails and the results are a map of type <String, Float>.
When returning the JSON object to the calling function (in this case, it's a flot diagram which requires [number,number] format), the key needs to be a number. This, in theory, is fine as my key to the map is a number in string form. I can't, however, figure out a way to create the JSON object with a numerical key.
I get results like this:
{"1":-9.814244910221474,"2":-9.710478606504552,"3":-9.636841089398253,"4":-9.524104819110796,"5":-9.522597036735684 ...}
instead of:
{1:-9.814244910221474,2:-9.710478606504552,3:-9.636841089398253,4:-9.524104819110796,5:-9.522597036735684 ...}
Does anyone know a way to force the JSON Map.encodeAsJSON() to produce an integer key? I've tried explicitly creating a map of type < integer,integer > before encoding it, and that doesn't work either.
Mike, Im looking at the json spec -- it appears that the keys must be strings. You should handle this client side.
http://www.json.org/