I'm using jackson's ObjectMapper to convert JSON files into Java objects.
How do I convert a JSON object that has an array as one of its fields? Example below:
{
"list":[
{
"value":"example"
},
{
"value":"example2"
}
]
}
ObjectMapper converts this into a LinkedHashMap with lines as the key and the value is an ArrayList with LinkedHashMaps, which have valueand example and so on.
Is there a way to read this json as an Object with a field list that is a List/Array containing objects which would fit (in this case, a simple object with String value as a field)?
Found a way to do this. This solution is based around Gson instead of ObjectMapper.
Basically Gson manages to convert arrays into the fields as I wanted it to happen here, instead of generating a lot of LinkedHashMaps.
You can achieve this with Jackson in a straight-forward way.
For this you need to model the JSON structure by some Java classes.
First, you need a class for modeling the whole JSON content
(let us call it Root) with a list property.
public class Root {
private List<Item> list;
// public getter and setter (omitted here for brevity)
}
Next, you need a class for modeling the list items
(let us call it Item) with a value property.
public class Item {
private String value;
// public getter and setter (omitted here for brevity)
}
Then you are able to read JSON content into a Java Root object by using
one of ObjectMapper's readValue(..., Class<T>) methods.
For example reading from a File:
ObjectMapper objectMapper = new ObjectMapper();
Root root = objectMapper.readValue(new File("example.json"), Root.class);
Related
For example, given JSON:
[
{"id":"3", "location":"NewYork", "date":"yesterday"},
{"id":"4", "location":"Moscow", "date":"today"}
]
resulting HashMap:
<"3", POJOLocation("NewYork", "yesterday")>
<"4", POJOLocation("Moscow", "today")>
where POJOLocation is a Java object:
class POJOLocation {
private String location;
private String date;
// etc
}
I've tried using custom deserializer, but it was really bloated with generic's tokens and hackish typeOfs. Perhaps there is a simple efficient solution?
Maybe create a POJOLocationId class:
class POJOLocationId {
private int id;
private String location;
private String date;
// etc
}
Then deserialize & loop over the resulting List populating your HashMap as you go?
Gson is designed to make serializing Java objects to their JSON equivalent painless. If you're trying to represent a Java data structure as a different type of JSON structure you're not going to have a lot of fun writing serializers and deserializers. At that point you might consider a lower-level JSON parser and simply implement the parsing you want yourself. Rather than representing your data one way in JSON and another way in Java (and thus running into the hassle of transforming between them) you might consider refactoring either your data structure or your data so they're more similar.
That said the easiest thing to do with Gson (which is really not that bad, memory and time-wise) is to use a wrapper type and then transform the input/output before using it. Something like so (borrowing from Tom Mac's type name):
private static final Type LIST_TYPE =
new TypeToken<List<POJOLocationId>>() {}.getType();
public String serialize(Map<Integer, POJOLocation> locations) {
List<POJOLocationId> locationsList = original.entrySet().stream()
.map(e -> new POJOLocationId(e.getKey(), e.getValue()).collect(toList());
return gson.toJson(locationsList);
}
public Map<Integer, POJOLocation> deserialize(String json) {
List<POJOLocationId> locationsList = gson.fromJson(json, LIST_TYPE);
return locationsList.stream()
.collect(toMap(l -> l.getId(), new POJOLocation(l)));
}
You certainly can get this same behavior with a custom deserializer, but this works, it's clean, and it's easy to read. The garbage collector should have no trouble cleaning up these temporary wrappers as soon as these methods return.
I have to deal with strange Json messages.
there are Arrays in the schema, but if there are only one element array becomes an string.
So sometimes it is:
"Cisco-AVPair": [
"connect-progress=Call Up",
"nas-tx-speed=8083000",
"nas-rx-speed=8083000"
],
and sometimes:
"Cisco-AVPair": "connect-progress=Call Up".
How to overcome this if I use Jackson 1.8.2
I am afraid I am not in control of source code generation and only can parse it.
I do parse it with:
mapper.readValue(json, refType);
while my type reference is:
#JsonProperty("Cisco-AVPair")
private List<String> CiscoAVPair = new ArrayList<String>();
#JsonProperty("Cisco-AVPair")
public List<String> getCiscoAVPair() {
return CiscoAVPair;
}
#JsonProperty("Cisco-AVPair")
public void setCiscoAVPair(List<String> CiscoAVPair) {
this.CiscoAVPair = CiscoAVPair;
}
As you see it is list of strings, but sometimes comes just as a string.
There's a specific config option even in ancient Jackson 1.8.2 that accomplishes exactly what you need.
You should configure your ObjectMapper instance to always deserialize JSON values as a List, no matter whether values come as an array or as a single element. Please see javadocs here for the deserialization feature you need to enable, and these other javadocs to see how to actually activate/deactivate a feature on an ObjectMapper instance.
ObjectMapper mapper = ...;
mapper = mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Bear in mind that configure() method returns another instance of ObjectMapper.
This is the json response returned by MediaWiki API. I want to create a class to be able to deserialize it to it use Jackson library. The problem is that this json contains a key which is different from each request (here is 290).
{
"query-continue": {
"revisions": {
"rvcontinue": 633308090
}
},
"query": {
"pages": {
"290": {
"pageid": 290,
"ns": 0,
"title": "A",
"revisions": [
{
"user": "Mr. Guye",
"timestamp": "2014-12-07T17:45:55Z",
"comment": "comment",
"contentformat": "text/x-wiki",
"contentmodel": "wikitext",
"*": "content"
}
]
}
}
}
}
How could create a class (or configure the mapper) to be able to deserialize this json?
You can deserialize JSON to multiple formats using Jackson. One way that you mentioned is to convert the JSON to a POJO which may be difficult when the keys are dynamic. Another approach is to deserialize the JSON to the Jackson Tree Model which is called JsonNode. The following illustrates how you can parse the provided JSON to a JsonNode and then retrieve the various attributes.
final ObjectMapper mapper = new ObjectMapper();
// Parse the JSON, deserialize to the Tree Model
final JsonNode jsonNode = mapper.readTree(jsonString);
// Get hold of the "query -> pages" node.
final JsonNode pages = jsonNode.path("query").path("pages");
// Iterate the pages
for (final JsonNode page : pages) {
// Work with the page object here...
System.out.println(page.get("pageid")); // -> 290
}
The JsonNode object is very flexible and contains various convenience functions for accessing the data. As shown in the example above the path() and get() methods are two ways of accessing the data. If you use get() the property MUST exist, if you use path the property MAY exist. Furthermore, there are multiple ways of iterating the sub-elements and the loop shown above is one way.
Take a look at the Jackson docs for more info.
The short answer is you can't, at least not in the current format with that abominable asterisk being present. Therefore, we will have to employ a bit of hackery here to get the job done, and I warn you upfront, it's not going to be pretty.
Firstly, copy that response, then go to http://www.jsonschema2pojo.org/ and paste it into the JSON textbox. After pasting it, change the asterisk to something more civilized, like "content". Select JSON (default is JSON Schema) for the Source Type, input your package and root class name respectively, and click JAR to generate the package with all the POJO's that map to this JSON. You could also click "Preview" and copy paste the code into your source files -- it's really up to you.
Now that we have a valid version of this JSON structure, we use Jackson to read it in. If your JSON String is called jsonResponse and the corresponding POJO class is MediaWiki, then you convert it with Jackson like this:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MediaWiki mw = objectMapper.readValue(profileJson, MediaWiki.class);
The key here is the FAIL_ON_UNKNOWN_PROPERTIES being set to false, which means it will ignore that asterisk, and create everything else for you.
Now, to actually grab whatever value was present for that asterisk and store it into our "content" attribute (or whatever else you wanted replace the asterisk with), you are going to have to parse this sucker out client-side and pass it as a separate input parameter, and to do that, you will have to yank it out by calling something like this:
var content = query.pages.290.revisions["*"];
This content parameter is passed and stored it into your POJO's content attribute.
I know it's a lot of work, and if anyone else has a more elegant solution, please share. As I said, mine was not going to be pretty. :-)
This looks like key value pair.
You can use map in order to deserialize key value pairs:
public class Query {
private Map<Integer, Page> pages;
public Map<Integer, Page> getPages() {
return pages;
}
public void setPages(Map<Integer, Page> pages) {
this.pages = pages;
}
}
Jackson handles such deserialization by default.
I have such structure of object:
class A {
List<B> bees;
}
class B {
String с;
}
I'm using Gson parser which serializes such object into this string:
{"a":{"bees":[{"с":"text"}]}}
(with adding a root element "a")
API's format is a little bit different:
{"a":{"bees":[{"b":{"с":"text"}}]}}
I need to be able to parse such strings into A objects correctly.
By default B object (as a part of A) becomes not null, but empty (all fields are null) which is understandable, cause parser doesn't find any field "b" in it (when it is actually a class name).
I'm looking for a general solution for that, I have a lot of such complex objects and I don't want to implement many custom deserializers for each of them.
Gson is not obligatory, I can use another lib if it's necessary.
Thanks.
I prefer Jackson Tree Model and JDK 8 Stream to parse such json string, the core idea is to map the bees array element {"b":{"с":"text"}} to {"с":"text"} by the functional map() API.
ObjectMapper mapper = new ObjectMapper();
JsonNode beesNode = mapper.readTree(jsonString).get("a").get("bees");
List<JsonNode> bs = StreamSupport.stream(beesNode.spliterator(), false)
.map(bee -> bee.get("b")).collect(Collectors.toList());
If it is possible for you to change the class structure then change the class structure so as to match the API's format. For example,
class A {
List<B> bees;
}
class B {
MyTypeObjB B;
}
class MyTypeObjB {
String c;
}
EDIT As per the comments, you can also try to customize deserialize method by implementing JsonDeserializer interface on your custom class.
You can find detail information how it can be performed on,
custom-json-deserializer
gson-deserialiser-example
I want to move from org.json to org.codehaus.jackson. How do I convert the following Java code?
private JSONObject myJsonMessage(String message){
JSONObject obj = new JSONObject();
obj.put("message",message);
return obj;
}
I left out the try-catch block for simplicity.
Instead of JSONObject use Jackson's ObjectMapper and ObjectNode:
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = mapper.createObjectNode();
node.put("message", "text");
This would be Jackson's equivalent of your current org.json code.
However, where Jackson really excels is in its capacity to do complex mappings between your Java classes (POJOs) and their JSON representation, as well as its streaming API which allows you to do really fast serialization, at least when compared with org.json's counterparts.
There is no JSONObject in Jackson api. Rather than returning a JSONObject, you can either return a Map or a Java Bean with message property that has getters and setters for it.
public class MyMessage {
private String message;
public void setMessage(final String message) {
this.message = message;
}
public String getMessage() {
return this.message;
}
}
So, your method will be reduced to:
private MyMessage(String message) {
MyMessage myMessage = new MyMessage();
myMessage.setMessage(message);
return myMessage;
}
Another aspect of this change would be changing the serialization code, to convert MyMessage back to json string. Jackson does Java Beans, maps by default, you don't need to create a JSONObject e.g.,
private String serializeMessage(MyMessage message){
//Note: you probably want to put it in a global converter and share the object mapper
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(message);
}
The above will return {message: "some message"}
I have skipped the exceptions for brevity.
If you want to upgrade from org.json library to Jackson piece by piece, and initially retaining same API, you might want to read "Upgrade from org.json to Jackson".
This would at least make your code about 3x faster for basic JSON reading and writing; plus you could -- if you so choose -- start converting processing, as Jackson makes it easy to convert between Trees and POJOs (ObjectMapper.treeToValue(...), valueToTree, convertValue between POJOs etc. etc).
Just keep in mind that tools that you are familiar with may bias your thinking to certain patterns, and keeping an open mind can help you find even better ones.
In case of Jackson (or GSON or other mature Java JSON tools), you really should consider where proper data-binding could help, instead of using JSON-centered tree model (that org.json offers). Tree Models keep your thinking grounded to JSON structure, which is sometimes useful; but might also prevent you from seeing more natural patterns that come from defining POJO structure to reflect expected JSON, and operating on Java Objects directly.