I need to be able to deserialize a JSON string produced by Jackson (Java/Spring server) with a C#/JSON.Net client while keeping the object references intact. Jackson uses "#id":1...n for cyclic references, while the reference is denoted by a single integer. JSON.Net uses "$id" and "$ref".
Does anybody know how to convert a JSON string from Jackson to a JSON.Net compatible version?
Here is my solution:
Use these settings for JSON.Net:
JsonSerializerSettings settings = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
Use this interceptor to convert the references:
public static class JSONInterceptor
{
public static string sanitizeJSON(string originalJSONFromJava)
{
// Get ID right from Jackson to JSON.Net
string pattern = Regex.Escape(#"""#id"":") + "(\\d+)";
string replacement = #"""$id"":""$1""";
Regex rgx = new Regex(pattern);
string output = rgx.Replace(originalJSONFromJava, replacement);
// Convert Jackson reference in array
pattern = #",(\d+)";
replacement = #",{""$ref"":""$1""}";
rgx = new Regex(pattern);
output = rgx.Replace(output, replacement);
// Convert single Jackson reference to ref
pattern = Regex.Escape(#"""\\w+"":") + "(\\d+)";
replacement = #"""$ref"":""$1""";
rgx = new Regex(pattern);
output = rgx.Replace(output, replacement);
return output;
}
}
Call the interceptor and deserialize like this:
asset = JsonConvert.DeserializeObject<Asset>(JSONInterceptor.sanitizeJSON(response), settings);
The asset class has this layout:
public class Asset {
....
// Parent asset
public Asset parent;
// Asset agents
public List<Agents> agent;
....
}
So, Jackson produces something like:
{"#id":1,......."parent":{"#id":15,.....},"agents":[{"#id":6, ......},12,{...}]...}
which needs to be converted into something like (JSON.Net):
{"$id":"1",...,"$ref":"15",....,"agents":[{...,"$ref":"6",...]}
This is what the code above does.
Hope this helps somebody.
Related
I have this String Json Payload
[
"key1":{
"atr1":"key1",
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
"key2":{
"atr1":"key2",
"atr2":"value5",
"atr3":"value6",
"atr4":value7,
"atr5":"value8"
}
]
and I want it to be converted in to the following format using Java
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3,
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4": "value7",
"atr5":"value8"
}
]
What would be the simplest way of transforming this ?
You cannot, because the example below is not valid json.
Check it out using this JSON validator.
If you paste this in (I've fixed some basic errors with lack of quotes)
{
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
}
You will get these errors ...
It can work if you change the target schema to something like this by using a json-array to contain your data.
[
{
"atr2":"value1",
"atr3":"value2",
"atr4":"value3",
"atr5":"value4"
},
{
"atr2":"value5",
"atr3":"value6",
"atr4":"value7",
"atr5":"value8"
}
]
If this works for you, then this problem can easily be solved by using the ObjectMapper class.
You use it to deserealize the original JSON into a class, which has two fields "key1" and "key2"
Extract the values of these fields and then just store them in an array ...
Serialize the array using the ObjectMapper.
Here a link, which explains how to use the ObjectMapper class to achieve the goals above.
EDIT:
So you'll need the following classes to solve the problem ...
Stores the object data
class MyClass {
String atr2;
String art3;
}
Then you have a container class, which is used to store the initial json.
class MyClassContainer {
MyClass key1;
MyClass key2;
}
Here's how you do the parse from the original json to MyClassContainer
var mapper = new ObjectMapper()
var json = //Get the json String somehow
var myClassContainer = mapper.readValue(json,MyClassContainer.class)
var mc1 = myClassContainer.getKey1();
var mc2 = myClassContainer.getKey2();
var myArray = {key1, key2}
var resultJson = mapper.writeValueAsString(myArray)
Assuming that you will correct the JSON into a valid one (which involves replacing the surrounding square braces with curly ones, and correct enclosure of attribute values within quotes), here's a simpler way which involves only a few lines of core logic.
try{
ObjectMapper mapper = new ObjectMapper();
mapper.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
HashMap<String, Data> map = mapper.readValue( jsonString, new TypeReference<HashMap<String, Data>>(){} );
String json = mapper.writeValueAsString( map.values() );
System.out.println( json );
}
catch( JsonProcessingException e ){
e.printStackTrace();
}
jsonString above is your original JSON corrected and valid JSON input.
Also notice the setting of FAIL_ON_UNKNOWN_PROPERTIES to false to allow atr1 to be ignored while deserializing into Data.
Since we are completely throwing away attr1 and its value, the Data class will represent all fields apart from that.
private static class Data{
private String atr2;
private String atr3;
private String atr4;
private String atr5;
}
Is there a way to make Jackson interpret single JSON object as an array with one element and vice versa?
Example, I have 2 slightly different formats of JSON, I need both to map to same Java object:
Format A (JSON array with one element):
points : [ {
date : 2013-05-11
value : 123
}]
Format B (JSON object, yes I know it looks "wrong" but it's what I'm given):
points : {
date : 2013-05-11
value : 123
}
Target Java object that both of the above should convert to:
//Data.java
public List<Point> points;
//other members omitted
//Point.java
class Point {
public String date;
public int value;
}
Currently, only A will parse properly to Data. I want avoid directly tampering with the JSON itself. Is there some configuration in Jackson I can tamper with in order to make it accept B ?
Try with DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY - it should work for you.
Example:
final String json = "{\"date\" : \"2013-05-11\",\"value\" : 123}";
final ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
final List<Point> points = mapper.readValue(json,
new TypeReference<List<Point>>() {});
The Jackson 1.x-compatible version uses DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY. So the above answer changes to:
final String json = "{\"date\" : \"2013-05-11\",\"value\" : 123}";
final ObjectMapper mapper = new ObjectMapper()
.enable(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
final List<Point> points = mapper.readValue(json,
new TypeReference<List<Point>>() {
});
System.out.println(points);
Can solve the above problem by this code is given below, this works
final ObjectMapper objectMapper = new
ObjectMapper().enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
objectMapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
try {
String jsonInString = objectMapper.writeValueAsString(products.get(j));
InventoryParser inventoryParser = objectMapper.readValue(jsonInString,
InventoryParser.class);
System.out.println(inventoryParser.getId());
}
catch (IOException e)
{
e.printStackTrace();
}
"InventoryParser" is a POJO Class.
"products.get(j)" is JSON String.
I have a Java POJO
public class TagBean {
private String type;
private String id;
public TagBean(String type, String id) {
this.type = type;
this.id = id;
}
// getters
// setters
}
I'm building pojo's and adding them to a List, as
....
List<TagBean> channelsList = new ArrayList<>();
List<TagBean> showsList = new ArrayList<>();
for each <business logic> {
if value=channels {
channelsList.add(new TagBean(...));
}
if value=shows {
showsList.add(new TagBean(...));
}
}
Gson gson = new GsonBuilder().create();
JsonObject tjsonObject = new JsonObject();
tjsonObject.addProperty("channels", gson.toJson(channelsList));
tjsonObject.addProperty("shows", gson.toJson(showsList));
JsonObject mainjsonObject = mainjsonObject.add("tags", tjsonObject);
return mainjsonObject;
My output is:
{
"tags": {
"channels": "[{\"type\":\"channel\",\"id\":\"channel\",\"name\":\"Channel\",\"parent\":\"SXM\"}]",
"shows": "[{\"type\":\"shows\",\"id\":\"shows\",\"name\":\"Shows\",\"parent\":\"SXM\"},{\"type\":\"shows\",\"id\":\"howard\",\"name\":\"Howard Stern\",\"parent\":\"shows\"},{\"type\":\"shows\",\"id\":\"howardstern\",\"name\":\"Howard Stern\",\"parent\":\"howard\"}]",
"sports": "[]"
}
}
How can i remove the backslashes? So the output is like:
{
"tags": {
"channels": " [{"type":"channel","id":"channel","name":"Channel","parent":"SXM"}]",
"shows": "[{"type":"shows","id":"shows","name":"Shows","parent":"SXM"},{"type":"shows","id":"howard","name":"Howard Stern","parent":"shows"}....
There were few other posts, but none explained this.
The problem is caused by this:
tjsonObject.addProperty("channels", gson.toJson(channelsList));
What that is doing is converting channelsList to a string containing a representation of the list in JSON, then setting the property to that string. Since the string contains JSON meta-characters, they must be escaped when the strings are serialized ... a second time.
I think that you need to do this instead:
tjsonObject.add("channels", gson.toJsonTree(channelsList));
That should produce this:
{
"tags": {
"channels":
[{"type":"channel","id":"channel","name":"Channel","parent":"SXM"}],
"shows":
[{"type":"shows","id":"shows","name":"Shows","parent":"SXM"},
{"type":"shows","id":"howard","name":"Howard Stern","parent":"shows"}
....
That is slightly different to what your question asked for, but it has the advantage of being syntactically valid JSON!
String mainJsonStr = mainjsonObject.toString();
mainJsonStr = mainJsonStr.replace("\\\\", ""); //replace the \
System.out.println(mainJsonStr);
The problem is that gson.toJson returns a String, and
tjsonObject.addProperty("channels", gson.toJson(channelsList));
this will add channels as a string and not as a JSON object.
One possible solution is to convert the string returned from gson.toJson to JSON object first then add it to the parent JSON object like
Gson gson = new GsonBuilder().create();
JsonObject tjsonObject = new JsonObject();
tjsonObject.put("channels", new JsonObject(gson.toJson(channelsList)));
tjsonObject.put("shows", new JsonObject(gson.toJson(showsList)));
this will treat channels and shows as JSON object
All strings in java have to escape quotes in them. So jsonInString should have slashes in it. When you output jsonInString though it shouldn't have the quotes. Are you looking at it in a debugger or something?
Just parse json directly and check - will get the output
above solution is not working anymore since GSON 2.8.*
use gson.toJsonTree(jsonText).getAsString(); instead
I am getting the data from the Zookeeper node like this
byte[] bytes = client.getData().forPath("/my/example/node1");
String ss = new String(bytes);
Here ss will have data like this which is a simple JSON String consisting of key value pair -
{"description":"Some Text", "machinename":"machineA", "ipaddress":"192.128.0.0"}
Now I want to append one more key value pair at the end to the above JSON String. This is the below key value pair I want to append -
"version":"v3"
So the final JSON String will look like this -
{"description":"Some Text", "machinename":"machineA", "ipaddress":"192.128.0.0", "version":"v3"}
What's the best and efficient way to do this?
Use a JSON Parser/Generator to parse your given JSON to a tree structure and then add your JSON field.
With Gson, that would look something like this
Gson gson = new Gson();
JsonObject jsonObject = gson.fromJson(ss, JsonObject.class); // parse
jsonObject.addProperty("version", "v3"); // modify
System.out.println(jsonObject); // generate
prints
{"description":"Some Text","machinename":"machineA","ipaddress":"192.128.0.0","version":"v3"}
Will Zookeeper always return valid JSON or their custom format? Be aware of that.
When it comes to JSON processing, string manipulation only works in special and simple cases. For the general case, a good JSON parser library should be used.
Jackson is among the top of such libraries in terms of performance, efficiency, versatility and reliability, plus it is published under the commercial-friendly Apache 2.0 license.
Following is a simple implementation of the requested answer in Jackson.
public static void main(String[] args)
{
String ss = "{\"description\":\"Some Text\", \"machinename\":\"machineA\", \"ipaddress\":\"192.128.0.0\"}";
System.out.println("JSON string before: " + ss);
try
{
ObjectMapper mapper = new ObjectMapper();
Map<String, String> map = (Map<String, String>)mapper.readValue(ss, Map.class);
map.put("version", "v3");
ss = mapper.writeValueAsString(map);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println("JSON string after: " + ss);
}
Basic string manipulation. Insert your additional string before the final close brace }. Make sure to add a comma.
Json objects don't need to be ordered.
String json = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
String json2 = "\"version\":\"v3\"";
json2 = ',' + json2;
String json3 = json.substring(0,json.length()-1) + json2 + json.charAt(json.length()-1);
That should be the simplest, most efficient way, if that's all you need to do.
For additional reading on String manipulation,
http://docs.oracle.com/javase/tutorial/java/data/manipstrings.html
Can you recommend on a Json Deserializer that can deserialize into existing object (merge 2 objects)?
When the user submit a form I want to save that into the db this way:
this is the json from the client:
{"affiliateId":1,"name":"First Affiliate","email":"email#gmail.com","user.userName":"test","user.password":"pass-hashed","employee.employeeId":1}
Affiliate affiliateFromDb = affiliateApi.getFromDbById(1);
SomeDeserialization json = new SomeDeserialization();
affiliateFromDb = json.fromJson(affiliateFromJson , affiliateFromDb );//affiliateFromDb = target bean
Meaning that I want the affiliateFromJson to be interpolated into affiliateFromDb.
And than I will call
affiliateApi.save(affiliateFromDb);
Note that the json contains deep deserialize, user.userName
Thanks
Use Gson! In particular, see the Object Examples.
class BagOfPrimitives {
private int value1 = 1;
private String value2 = "abc";
private transient int value3 = 3;
BagOfPrimitives() {
// no-args constructor
}
}
BagOfPrimitives obj2 = gson.fromJson(json, BagOfPrimitives.class);
The only catch here — but you will have this same problem with any other JSON (de)serializer — is the nonstandard "deep" object format you want to work with. You would have to use something like this instead:
{"affiliateId":1,"name":"First Affiliate","email":"email#gmail.com","user": {"userName":"test","password":"pass-hashed"},"employee.employeeId":1}
http://www.json.org/javadoc/org/json/JSONObject.html
JSONObject jsonResponse = new JSONObject(responseString);