How Jackson-Creating a JsonObject - java

I want to create a JsonObject like this:
{
Response: 200,
Lists: [
{
Test: "Math",
Result: "6",
Credit: "3"
},
{
Test: "C++",
Result: "10",
Credit: "6"
}
]
}
I know create this with lib org.json but with Jackson? i try to use
JsonNodeFactory nodeFactory = new JsonNodeFactory();
but i have this problem
The constructor JsonNodeFactory() is not visible

Make sure to use the latest version of Jackson. They moved from codehaus to FasterXML: http://wiki.fasterxml.com/JacksonHome.
You don't need to instantiate the factory. You can use the public static one: com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.
JsonNodeFactory factory = JsonNodeFactory.instance;
ObjectNode root = factory.objectNode();
root.put("Response", 200);
ArrayNode list = factory.arrayNode();
list.add(...);
...
root.set("List", list);
Note that Jackson is a great library to map Java POJOs to JSON (and back). Rather than creating the JSON structure by hand, you can create Java classes that Jackson will serialize to JSON:
public class Item {
#JsonProperty("Test")
private String test;
#JsonProperty("Result")
private String result;
#JsonProperty("Credit")
private String credit;
}
public class Root {
#JsonProperty("Response")
private int response;
#JsonProperty("List")
private List<Item> list;
}
public static void main(String[] args) {
Root root = new Root();
...
String json = new ObjectMapper().writeValueAsString(root)
}

To create a JsonNode object use ObjectMapper. For example:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readValue(JSON_STRING, JsonNode.class)
Refer to the Jackson documentation for information.

Related

Deserialize Lists and Objects to the same Structure with Jackson

As input for my Application I might get either a single JsonObject, or a List of them:
input1 = [ { "prop": "val1" }, { "prop": "val2" } ]
input2 = { "prop": "val" }
I can use JsonNode as target type for both inputs
objectMapper.readValue(input1, JsonNode.class);
objectMapper.readValue(input2, JsonNode.class);
And then evaluate whether the root node is a ArrayNode or ObjectNode.
I seek a way to define my custom target type, like a List<MyObject> which has one Element if a JsonObject is provided, or zero to multiple, if a List is provided.
objectMapper.readValue(input, new TypeRef<ArrayList<MyObject>>() {});
however fails for the single object - it can not construc an Array-Type from {.
I was trying to create my own type:
public class MyList extends ArrayList<MyObject> {
public String prop;
#JsonCreator
public MyList(String prop) {
super();
this.prop = prop; // Resp add(new MyObject(prop));
}
public MyList() {}
}
But Jackson refuses to use the JsonCreator for single objects.
Is there any way, I could do that (ideally without a custom serializer, unless that one can be made pretty generic)
Of course, Jackson has an easy solution for that:
DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to your help!
#EqualsAndHashCode
public class Example {
#JsonProperty public String name
}
#Test
public void experiment() {
ObjectMapper om = new ObjectMapper();
om.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
String list= "[{ \"name\": \"peter\" }]";
String single= "{ \"name\": \"peter\" }";
List<Example> respList = om.readValue(list, new TypeReference<List<Example>>() {});
List<Example> respSingle = om.readValue(single, new TypeReference<List<Example>>() {});
Assert.assertEquals(respList, respSingle)
}

Jackson -- parse json using xpath or similar

I have some json and it's fairly complex -- (a bit too complex and open-ended to model using something like gson), and I need to extract string values from certain nodes into a list of strings.
The following code works, but due to the way my json works -- it's grabbing lots of extra stuff that I don't want (note: I don't own the json schema)
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
List<JsonNode> keys = node.findValues("key") ;
for(JsonNode key: keys){
System.out.println(key.toString());
}
The contents of Json is fairly complex (Jira filter export) which looks like this:
{
"issues": [
{
"key":"MIN-123",
...
"fields":{
"key":"A_Elric"
}
}
]
}
Assertions:
I always want to extract issues[x].key and not any of the subkeys. I would prefer to extract this into a list, but any normal data structure is fine. I'm already using Jackson -- but gson is also an option if there's a sane way of doing so.
Thanks for the assist!
JsonPath is xpath for json, and it has a Java implementation.
Here is a working example to get issue keys without subkeys:
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
public class JsonPathTest {
public static String ROOT_ARRAY = "issues";
public static String KEY = "key";
// get all KEYs right under ROOT array
public static String jsonPath = String.format("$.%s[*].%s", ROOT_ARRAY, KEY);
public static void main(String[] args) {
try {
String jsonStr = new String(Files.readAllBytes(Paths.get("c:/temp/xx.json")));
Object jsonObj = Configuration.defaultConfiguration().jsonProvider().parse(jsonStr);
List<String> keys = JsonPath.parse(jsonObj).read(jsonPath);
System.out.println(keys);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class ExportFilter{
private static final String KEY = "key";
private List<Map<String,Object>> issues = new ArrayList<>();
//getters and setters
#JsonIgnore
public List<String> getKeys(){
return issues.stream()
.map(issue-> issue.get(KEY))
.filter(Objects::nonNull)
.map(Objects::toString)
.collect(toList());
}
}
Example usage:
ObjectMapper objectMapper = new ObjectMapper();
List<String> keys = objectMapper.readValue( .., ExportFilter.class).getKeys();

ObjectMapper readValue

I load a ressource file json
with the text format
{
"sources": [{
"prop1": "1",
"prop2": "2"
},
{
"prop1": "1",
"prop2": "2"
},
],
"redirection": [{
"prop1": "1",
"prop2": "2"
}
]
}
I have a class with this properties prop1 and prop2
I want to recover with ObjectMapper a list class. What the method ?
This code doesn't work ....
Map<String, Object> mp = mapper.readValue(jsonResource.getInputStream(),new TypeReference<Map<String, Object>>() {});
String sourceText= new ObjectMapper().readTree(jsonResource.getInputStream()).get("sources").asText();
mapper.readValue(sourceText, new TypeReference<List<MyClass>>(){});
Thanks for your help
In your case, I would write a custom JsonDeserializer. Haven't really tested the code, but I think the idea is clear:
final MyClassDeserializer myClassDeserializer = new MyClassDeserializer();
final SimpleModule deserializerModule = new SimpleModule();
deserializerModule.addDeserializer(MyClass.class, myClassDeserializer);
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(deserializerModule);
And the code for JsonDeserializer:
public class MyClassDeserializer extends JsonDeserializer<MyClass> {
#Override
public MyClass deserialize(final JsonParser jsonParser, final DeserializationContext context)
throws IOException {
final JsonNode node = jsonParser.getCodec().readTree(jsonParser);
final JsonNode sourcesNode = node.get("sources");
if(node.isArray()) {
final ArrayNode arrayNode = (ArrayNode) node;
final Iterable<JsonNode> nodes = arrayNode::elements;
final Set<Source> set = StreamSupport.stream(nodes.spliterator(), false)
.map(mapper)
.collect(Collectors.toSet());
...
}
...
}
First thing: Your JSON is invalid. There is a comma after the second object in the sources array. This has to be deleted.
Second: I think you didn't choose the right type for your result. What your JSON represents is a map which maps from string to an array of objects. So the type should be something like Map<String, Props[]> (Since you didn't provide the name of your class, I called it Props.
With these considerations you can construct a MapType by using ObjectMappers getTypeFactory() method and deserialize the value using the constructed type like shown below.
ObjectMapper mapper = new ObjectMapper();
TypeFactory typeFactory = mapper.getTypeFactory();
MapType mapType = typeFactory.constructMapType(HashMap.class, String.class, Props[].class);
Map<String, Props[]> map = mapper.readValue(s, mapType);
I actually voted for the other answer, but this is my idea, to create the classes and let jackson do the work :
public class ResourceTest {
#Test
public void test1() throws IOException {
assertTrue(true);
Resource resource = new Resource();
resource.getRedirectrions().add(makeRedirectrion("rprop11", "rprop12"));
resource.getRedirectrions().add(makeRedirectrion("rprop21", "rprop22"));
resource.getSources().add(makeSource("sprop11","sprop12"));
resource.getSources().add(makeSource("sprop21","sprop22"));
String json = new ObjectMapper().writeValueAsString(resource);
System.out.println(json);
Resource resource1 = new ObjectMapper().readValue(json, Resource.class);
System.out.println(resource1);
}
private Source makeSource(String prop1, String prop2) {
Source source = new Source();
source.setProp1(prop1);
source.setProp2(prop2);
return source;
}
private Redirectrion makeRedirectrion(String prop1, String prop2) {
Redirectrion redirectrion = new Redirectrion();
redirectrion.setProp1(prop1);
redirectrion.setProp2(prop2);
return redirectrion;
}
}
Output is:
{"sources":[{"prop1":"sprop11","prop2":"sprop12"},{"prop1":"sprop21","prop2":"sprop22"}],"redirectrions":[{"prop1":"rprop11","prop2":"rprop12"},{"prop1":"rprop21","prop2":"rprop22"}]}
Resource{sources=[Source{prop1='sprop11', prop2='sprop12'}, Source{prop1='sprop21', prop2='sprop22'}], redirectrions=[Source{prop1='rprop11', prop2='rprop12'}, Source{prop1='rprop21', prop2='rprop22'}]}

Encode/decode JSON keys?

I want to send a minified version of my JSON by minifying the keys.
The Input JSON string obtained after marshalling my POJO to JSON:
{
"stateTag" : 1,
"contentSize" : 10,
"content" : {
"type" : "string",
"value" : "Sid"
}
}
Desired JSON STRING which I want to send over the network to minimize payload:
{
"st" : 1,
"cs" : 10,
"ct" : {
"ty" : "string",
"val" : "Sid"
}
}
Is there any standard way in java to achieve this ??
PS: My json string can be nested with other objects which too I will have to minify.
EDIT:
I cannot change my POJOs to provide annotations. I have XSD files from which I generate my java classes. So changing anything there is not an option.
You can achieve this in Jackson by using #JsonProperty annotation.
public class Pojo {
#JsonProperty(value = "st")
private long stateTag;
#JsonProperty(value = "cs")
private long contentSize;
#JsonProperty(value = "ct")
private Content content;
//getters setters
}
public class Content {
#JsonProperty(value = "ty")
private String type;
#JsonProperty(value = "val")
private String value;
}
public class App {
public static void main(String... args) throws JsonProcessingException, IOException {
ObjectMapper om = new ObjectMapper();
Pojo myPojo = new Pojo(1, 10, new Content("string", "sid"));
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(myPojo));
}
Outputs:
{
"st" : 1,
"cs" : 10,
"ct" : {
"ty" : "string",
"val" : "sid"
}
}
SOLUTION 2 (Using Custom Serializer):
This solution is specific to your pojo, it means for every pojo you will need a new serializer.
public class PojoSerializer extends JsonSerializer<Pojo> {
#Override
public void serialize(Pojo pojo, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
/* your pojo */
jgen.writeStartObject();
jgen.writeNumberField("st", pojo.getStateTag());
jgen.writeNumberField("cs", pojo.getContentSize());
/* inner object */
jgen.writeStartObject();
jgen.writeStringField("ty", pojo.getContent().getType());
jgen.writeStringField("val", pojo.getContent().getValue());
jgen.writeEndObject();
jgen.writeEndObject();
}
#Override
public Class<Pojo> handledType() {
return Pojo.class;
}
}
ObjectMapper om = new ObjectMapper();
Pojo myPojo = new Pojo(1, 10, new Content("string", "sid"));
SimpleModule sm = new SimpleModule();
sm.addSerializer(new PojoSerializer());
System.out.print(om.registerModule(sm).writerWithDefaultPrettyPrinter().writeValueAsString(myPojo));
SOLUTION 3 (Using a naming strategy):
This solution is a general solution.
public class CustomNamingStrategy extends PropertyNamingStrategyBase {
#Override
public String translate(String propertyName) {
// find a naming strategy here
return propertyName;
}
}
ObjectMapper om = new ObjectMapper();
Pojo myPojo = new Pojo(1, 10, new Content("string", "sid"));
om.setPropertyNamingStrategy(new CustomNamingStrategy());
System.out.print(om.writerWithDefaultPrettyPrinter().writeValueAsString(myPojo));
Use the annotations...
with gson:
adding #SerializedName("st") over the Class Member will serialize the variable stateTag as "st" : 1, it doesnt matter how deep in the json you are going to nest the objects.

Parsing nested JSON data using GSON

I'm trying to parse some JSON data using gson in Java that has the following structure but by looking at examples online, I cannot find anything that does the job.
Would anyone be able to assist?
{
"data":{
"id":[
{
"stuff":{
},
"values":[
[
123,
456
],
[
123,
456
],
[
123,
456
],
],
"otherStuff":"blah"
}
]
}
}
You just need to create a Java class structure that represents the data in your JSON. In order to do that, I suggest you to copy your JSON into this online JSON Viewer and you'll see the structure of your JSON much clearer...
Basically you need these classes (pseudo-code):
class Response
Data data
class Data
List<ID> id
class ID
Stuff stuff
List<List<Integer>> values
String otherStuff
Note that attribute names in your classes must match the names of your JSON fields! You may add more attributes and classes according to your actual JSON structure... Also note that you need getters and setters for all your attributes!
Finally, you just need to parse the JSON into your Java class structure with:
Gson gson = new Gson();
Response response = gson.fromJson(yourJsonString, Response.class);
And that's it! Now you can access all your data within the response object using the getters and setters...
For example, in order to access the first value 456, you'll need to do:
int value = response.getData().getId().get(0).getValues().get(0).get(1);
Depending on what you are trying to do. You could just setup a POJO heirarchy that matches your json as seen here (Preferred method). Or, you could provide a custom deserializer. I only dealt with the id data as I assumed it was the tricky implementation in question. Just step through the json using the gson types, and build up the data you are trying to represent. The Data and Id classes are just pojos composed of and reflecting the properties in the original json string.
public class MyDeserializer implements JsonDeserializer<Data>
{
#Override
public Data deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
final Gson gson = new Gson();
final JsonObject obj = je.getAsJsonObject(); //our original full json string
final JsonElement dataElement = obj.get("data");
final JsonElement idElement = dataElement.getAsJsonObject().get("id");
final JsonArray idArray = idElement.getAsJsonArray();
final List<Id> parsedData = new ArrayList<>();
for (Object object : idArray)
{
final JsonObject jsonObject = (JsonObject) object;
//can pass this into constructor of Id or through a setter
final JsonObject stuff = jsonObject.get("stuff").getAsJsonObject();
final JsonArray valuesArray = jsonObject.getAsJsonArray("values");
final Id id = new Id();
for (Object value : valuesArray)
{
final JsonArray nestedArray = (JsonArray)value;
final Integer[] nest = gson.fromJson(nestedArray, Integer[].class);
id.addNestedValues(nest);
}
parsedData.add(id);
}
return new Data(parsedData);
}
}
Test:
#Test
public void testMethod1()
{
final String values = "[[123, 456], [987, 654]]";
final String id = "[ {stuff: { }, values: " + values + ", otherstuff: 'stuff2' }]";
final String jsonString = "{data: {id:" + id + "}}";
System.out.println(jsonString);
final Gson gson = new GsonBuilder().registerTypeAdapter(Data.class, new MyDeserializer()).create();
System.out.println(gson.fromJson(jsonString, Data.class));
}
Result:
Data{ids=[Id {nestedList=[[123, 456], [987, 654]]}]}
POJO:
public class Data
{
private List<Id> ids;
public Data(List<Id> ids)
{
this.ids = ids;
}
#Override
public String toString()
{
return "Data{" + "ids=" + ids + '}';
}
}
public class Id
{
private List<Integer[]> nestedList;
public Id()
{
nestedList = new ArrayList<>();
}
public void addNestedValues(final Integer[] values)
{
nestedList.add(values);
}
#Override
public String toString()
{
final List<String> formattedOutput = new ArrayList();
for (Integer[] integers : nestedList)
{
formattedOutput.add(Arrays.asList(integers).toString());
}
return "Id {" + "nestedList=" + formattedOutput + '}';
}
}

Categories

Resources