I have a data structure BookCollection which has a Set of Books. Each book has a reference to the BookCollection that it's in. This is a standard one-to-many relationship. It works well on server side, but then I have to send it down to the client. The app uses Jackson ObjectMapper for serialization, set up as a Spring Bean. The problem, is that you can't trivially serialize a BookCollection and Books, since there are no references in standard JSON (and I do need standard JSON).
the current solution is to put #JsonIgnore on Book.collection field, or more advanced, #JsonManagedReference on BookCollection.books and #JsonBackReference on Book.collection. However this doesn't solve all my problems, some requests ask for a Book object, and want to get its BookCollection also.
Ultimately, I am looking for some way to tell the Serializer to only include each object once. Thus, a when I get a Book, the JSON would look something like this:
{
isbn: 125412416,
title: "Introduction to JSON",
collection: {
name: "Programming Books",
books: [
{
isbn: 18723425,
title: "Java for experts"
},
{
isbn: 82472347,
title: "C# and its extensions"
},
]
}
}
While "C# and it's extensions" and "Java for experts" also have a reference to the collection object, it is not serialized since it was serialized already. Also, the collection object doesn't include the "Introduction to JSON" book, since it was already serialized.
And when I ask for a BookCollection, I get this:
{
name: "Programming Book",
books: [
{
isbn: 125412416,
title: "Introduction to JSON"
},
{
isbn: 18723425,
title: "Java for experts"
},
{
isbn: 82472347,
title: "C# and its extensions"
},
]
}
Serialize fields once
While BookCollection serializes splendily, Book is a little confusing, since the catalog now has SOME books (missing the original). Even better would be to allow to specify to serialize each field once. Let Book.collection be allowed to serialize once
Serializing a Book will look like this:
{
isbn: 125412416,
title: "Introduction to JSON",
collection: {
name: "Programming Books",
books: [
{
isbn: 18723425,
title: "Java for experts"
},
{
isbn: 82472347,
title: "C# and its extensions"
},
{
isbn: 125412416,
title: "Introduction to JSON"
}
]
}
}
And serializng a book collection is similar:
{
name: "Programming Book",
books: [
{
isbn: 125412416,
title: "Introduction to JSON"
},
{
isbn: 18723425,
title: "Java for experts"
},
{
isbn: 82472347,
title: "C# and its extensions"
},
]
}
I recently encountered a similar problem: Jackson - serialization of entities with birectional relationships (avoiding cycles)
So the solution is to upgrade to Jackson 2.0, and add to classes the following annotation:
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class,
property = "#id")
public class SomeEntityClass ...
namespace ConsoleApplication4
{
public class Teacher
{
public int MyProperty { get; set; }
// [ScriptIgnore]
public ClassRoom working { get; set; }
}
public class ClassRoom
{
public ClassRoom()
{
}
public int sss { get; set; }
public int s2 { get; set; }
public int ddy { get; set; }
// [ScriptIgnore]
public Teacher teachers { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.ForegroundColor = ConsoleColor.Green;
ClassRoom #new = new ClassRoom();
Teacher #teacher = new Teacher();
#new.teachers = teacher;
#teacher.working = #new;
JavaScriptSerializer serializer = new JavaScriptSerializer();
List< ClassRoom> classrooms = new List<ClassRoom>();
classrooms.Add(#new);
classrooms.Add(#new);
classrooms.Add(#new);
classrooms.Add(#new);
classrooms.Add(#new);
var result = (from n in classrooms
select n).ToList();//tolist() to convert IEnumrable to list can be modified
for (int i = 0; i < result.Count; i++)
{
result[i].teachers.working = null;//making all child's parents =null to prevent circular refrencing
}
Console.WriteLine(serializer.Serialize(result));
Console.ReadLine();
}
}
}
I think this solution would solve your problem
it solved mine
( result[i].teachers.working = null;)
make the property that causes circular reference =null;
Related
I need to map a JSON object but the problem is that it has an inner custom list. How could use RestTemplate in this case?
I am trying to use ResponseEntity and ParameterizedTypeReference but I have not found the solution yet.
{
"results":{
"ALL":{
"currencyName":"Albanian Lek",
"currencySymbol":"Lek",
"id":"ALL"
},
"XCD":{
"currencyName":"East Caribbean Dollar",
"currencySymbol":"$",
"id":"XCD"
},
"EUR":{
"currencyName":"Euro",
"currencySymbol":"€",
"id":"EUR"
},
"BBD":{
"currencyName":"Barbadian Dollar",
"currencySymbol":"$",
"id":"BBD"
},
"BTN":{
"currencyName":"Bhutanese Ngultrum",
"id":"BTN"
},
"BND":{
"currencyName":"Brunei Dollar",
"currencySymbol":"$",
"id":"BND"
},
"XAF":{
"currencyName":"Central African CFA Franc",
"id":"XAF"
},
"CUP":{
"currencyName":"Cuban Peso",
"currencySymbol":"$",
"id":"CUP"
},
"USD":{
"currencyName":"United States Dollar",
"currencySymbol":"$",
"id":"USD"
}
}
}
// you can create a custom class like below and try to map it
class NodeWrapper{
private Map<String, NodeData> results;
}
class NodeData{
private String currencyName;
private String currencySymbol;
private id;
}
// also allow nulls using object mapper annotations
I'm still pretty new to programming with Kotlin but I can't seem to figure out the correct way to parse my JSON. I'm attempting to get "title" and "body" from "notification" in "unackd" array only.
So far I've got:
private fun parse(): Boolean {
try {
val ja = JSONArray(jsonData)
var jo: JSONObject
users.clear()
var user: User
for (i in 0 until ja.length()) {
jo = ja.getJSONObject(i)
val name = jo.getString("title")
val username = jo.getString("body")
user = User(username,name)
users.add(user)
}
return true
} catch (e: JSONException) {
e.printStackTrace()
return false
}
}
Meanwhile my JSON is structured as so:
{
"unackd": [
{
"notification": {
"title": "Title Test Number 200",
"body": "passage local they water difficulty tank industry allow increase itself captured strike immediately type phrase driver change save potatoes stems addition behavior grain trap rapidly love refused way television bright 1100"
},
"data": {
"id": "1100",
"phone": "+15555551234"
}
},
{
"notification": {
"title": "Title Test Number 199",
"body": "announced beside well noted mysterious farm he essential likely deeply vast touch 1099"
},
"data": {
"id": "1099",
"phone": "+15555551234"
}
}
],
"ackd": [
{
"notification": {
"title": "Title Test Number 200",
"body": "passage local they water difficulty tank industry allow increase itself captured strike immediately type phrase driver change save potatoes stems addition behavior grain trap rapidly love refused way television bright 1100"
},
"data": {
"id": "1100",
"phone": "+15555551234"
}
},
{
"notification": {
"title": "Title Test Number 199",
"body": "announced beside well noted mysterious farm he essential likely deeply vast touch 1099"
},
"data": {
"id": "1099",
"phone": "+15555551234"
}
}
]
}
I believe my issue is getting into "notification" to then get the strings "title" and "body". Which I've tried
test1 = jo.getJSONObject("notification")
Any help would be appreciated!
EDIT:
This is my logcat error, I assume it has to do with the JSON.typeMismatch:
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
at org.json.JSONArray.<init>(JSONArray.java:108)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
The exception message suggests that you're passing data that doesn't represent a JSON array when instantiating JSONArray:
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
The JSON you've attached is in fact a JSON object, notice that its content is enclosed in {}. Hence to access the "unackd" array, you need to first create a JSON object, and then reference the array inside of it:
val root = JSONObject(jsonData)
val ja = root.getJSONArray("unackd")
// the rest of your code goes here
Listen friend , parsing the JSON Object with JSON ARRAY with key (like: unackd , ackd) is so simple.
There are 2 ways:
1st Way)
Parse your JSON to Pojo schema
http://www.jsonschema2pojo.org/
public class Ackd {
#SerializedName("notification")
#Expose
private Notification_ notification;
#SerializedName("data")
#Expose
private Data_ data;
public Notification_ getNotification() {
return notification;
}
public void setNotification(Notification_ notification) {
this.notification = notification;
}
public Data_ getData() {
return data;
}
public void setData(Data_ data) {
this.data = data;
}
}
public class Data {
#SerializedName("id")
#Expose
private String id;
#SerializedName("phone")
#Expose
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
No need to Make all class for parsing (like ackd (Json Array))
2nd Way)
You need to PARSE JSON array with name only unackd not ackd.
String jsonStr = sh.makeServiceCall(url);
JSONObject jsonObj = new JSONObject(jsonStr);
// Getting JSON Array node
JSONArray unA= jsonObj.getJSONArray("unackd");
for (int i = 0; i < unA.length(); i++)
{
JSONObject c = unA.getJSONObject(i);
String title= c.getString("title");
String body= c.getString("body");
}
Auto generate Data class
http://www.jsonschema2pojo.org/
I suppose that your class is named Response.java
Response object=new Gson().fromjson(jsonContentFile,Response.class);
Following data classes are generated for your JSON using https://json2kotlin.com
data class Json4Kotlin_Base (
val unackd : List<Unackd>,
val ackd : List<Ackd>
)
and
data class Data (
val id : Int,
val phone : Int
)
and
data class Notification (
val title : String,
val body : String
)
and
data class Ackd (
val notification : Notification,
val data : Data
)
and
data class Unackd (
val notification : Notification,
val data : Data
)
Here's a video that explains how to implement these when generated.
how I can parse following code retrieved in a textbox:
{
"items": [
{
"snippet": {
"channelId": "UCcTbyoZjhqoCn4yVawpMFDA",
"title": "Forever - Stratovarius",
"categoryId": "10"
},
"statistics": {
"viewCount": "6180411",
"likeCount": "19060",
"dislikeCount": "342",
"favoriteCount": "0",
"commentCount": "3025"
}
}
]
}
My code for get value title and likeCount:
Dim url As String = "https://www.googleapis.com/youtube/v3/videos?id=8BvV9arABLs&key=KEYAPI&fields=items(id,snippet(channelId,title,categoryId),statistics)&part=snippet,statistics"
Dim json As String = New WebClient().DownloadString(url)
Dim root As JToken = JToken.Parse(json)
Dim sb As New StringBuilder()
For Each item As JToken In root("items")
textbox1.text=sb.AppendLine(item.SelectToken("title") & sb.AppendLine(item.SelectToken("likeCount"))
Next
First, it's better to format marked up data like JSON before you post it.
Like this:
{
"items" : [{
"snippet" : {
"channelId" : "UCcTbyoZjhqoCn4yVawpMFDA",
"title" : "Forever - Stratovarius",
"categoryId" : "10"
},
"statistics" : {
"viewCount" : "6180411",
"likeCount" : "19060",
"dislikeCount" : "342",
"favoriteCount" : "0",
"commentCount" : "3025"
}
}
]
}
As #rufanov said there are a lof of packages for JSON serialization.
As for me, I use Newtonsoft JSON. I use it in C# and will write examples in it, but I strongly believe it should be the same or similar on VB.NET.
Create classes for JSON objects. Pay attention: the members should be names as in JSON object.
public class JItemArray
{
public JItem[] items;
}
public class JItem
{
public JSnippet snippet;
public JStatistics statistics;
}
public class JSnippet
{
public string channelId;
public string title;
public string categoryId;
}
public class JStatistics
{
public string viewCount;
public string likeCount;
public string dislikeCount;
public string favoriteCount;
public string commentCount;
}
Then, you will be able to do
JItemArray itemArray = JsonConvert.DeserializeObject<JItemArray>(yourJsonString);
It will throw JsonReaderException (with explanation in Message) if something fails.
Else, it will return a proper JItem .NET object with all necessary parsed data.
Here it is on NuGet website.
https://www.nuget.org/packages/Newtonsoft.Json/
But if you only need these two values it's pretty good practice to use JToken.
There is many NuGet packages for working with JSON. Use one of them.
Ive tried the following things and they dont seem to work.
Ember.js Rest Adapter: mapping JSON with no root (.NET Web API)
How can I add a custom root node when serializing an object with JSON.NET?
Im using a web api controller for this.
This is my class and im returning a list of it:
[JsonObject(Title = "rootNamedObject")]
public class RootNamedObject
{
[JsonProperty("ObjectId")]
public int Id { get; set; }
[JsonProperty("Description")]
public string Description { get; set; }
}
Right now this is my result:
[
{
"ObjectId": 1,
"Description": "Description 1",
},
{
"ObjectId": 2,
"Description": "Description 2",
}
]
I need to generate this:
{
"rootNamedObject": [
{
"ObjectId": 1,
"Description": "Description 1"
}
Basically just add the name of the class to the result!
I was having this issue in ember and found the best solution for me was to build a new serializer and override the normalizePayload method. code below:
export default DS.RESTSerializer.extend({
normalizePayload: function(payload) {
var root = "posts";
var output = {};
output[root] = payload;
return output;
} });
This wraps the initial response and adds the root to it, hope it helps!
I'm using Jersey to create a REST web service for a server component.
The JAXB-annotated object I want to serialize in a list looks like this:
#XmlRootElement(name = "distribution")
#XmlType(name = "tDistribution", propOrder = {
"id", "name"
})
public class XMLDistribution {
private String id;
private String name;
// no-args constructor, getters, setters, etc
}
I have a REST resource to retrieve one distribution which looks like this:
#Path("/distribution/{id: [1-9][0-9]*}")
public class RESTDistribution {
#GET
#Produces("application/json")
public XMLDistribution retrieve(#PathParam("id") String id) {
return retrieveDistribution(Long.parseLong(id));
}
// business logic (retrieveDistribution(long))
}
I also have a REST resource to retrieve a list of all distributions, which looks like this:
#Path("/distributions")
public class RESTDistributions {
#GET
#Produces("application/json")
public List<XMLDistribution> retrieveAll() {
return retrieveDistributions();
}
// business logic (retrieveDistributions())
}
I use a ContextResolver to customize JAXB serialization, which is currently configured like this:
#Provider
#Produces("application/json")
public class JAXBJSONContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
public JAXBJSONContextResolver() throws Exception {
JSONConfiguration.MappedBuilder b = JSONConfiguration.mapped();
b.nonStrings("id");
b.rootUnwrapping(true);
b.arrays("distribution");
context = new JSONJAXBContext(b.build(), XMLDistribution.class);
}
#Override
public JAXBContext getContext(Class<?> objectType) {
return context;
}
}
Both REST resources work, as well as the context resolver. This is an example of output for the first one:
// path: /distribution/1
{
"id": 1,
"name": "Example Distribution"
}
Which is exactly what I want. This is an example of output for the list:
// path: /distributions
{
"distribution": [{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
}
Which is not quite what I want.
I don't understand why there is an enclosing distribution tag there. I wanted to remove it with .rootUnwrapping(true) in the context resolver, but apparently that only removes another enclosing tag. This is the output with .rootUnwrapping(false):
// path: /distribution/1
{
"distribution": {
"id": 1,
"name": "Example Distribution"
}
} // not ok
// path: /distributions
{
"xMLDistributions": {
"distribution": [{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
}
}
I also had to configure .arrays("distribution") to always get a JSON array, even with only one element.
Ideally, I'd like to have this as an output:
// path: /distribution/1
{
"id": 1,
"name": "Example Distribution"
} // currently works
// path: /distributions
[{
"id": 1,
"name": "Sample Distribution 1"
}, {
"id": 2,
"name": "Sample Distribution 2"
}]
I tried to return a List<XMLDistribution>, a XMLDistributionList (wrapper around a list), a XMLDistribution[], but I couldn't find a way to get a simple JSON array of distributions in my required format.
I also tried the other notations returned by JSONConfiguration.natural(), JSONConfiguration.mappedJettison(), etc, and couldn't get anything resembling what I need.
Does anyone know if it is possible to configure JAXB to do this?
I found a solution: replace the JAXB JSON serializer with a better behaved JSON serializer like Jackson. The easy way is to use jackson-jaxrs, which has already done it for you. The class is JacksonJsonProvider. All you have to do is edit your project's web.xml so that Jersey (or another JAX-RS implementation) scans for it. Here's what you need to add:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.project.packages;org.codehaus.jackson.jaxrs</param-value>
</init-param>
And that's all there is to it. Jackson will be used for JSON serialization, and it works the way you expect for lists and arrays.
The longer way is to write your own custom MessageBodyWriter registered to produce "application/json". Here's an example:
#Provider
#Produces("application/json")
public class JsonMessageBodyWriter implements MessageBodyWriter {
#Override
public long getSize(Object obj, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
#Override
public boolean isWriteable(Class type, Type genericType,
Annotation annotations[], MediaType mediaType) {
return true;
}
#Override
public void writeTo(Object target, Class type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap httpHeaders, OutputStream outputStream)
throws IOException {
new ObjectMapper().writeValue(outputStream, target);
}
}
You'll need to make sure your web.xml includes the package, as for the ready-made solution above.
Either way: voila! You'll see properly formed JSON.
You can download Jackson from here:
http://jackson.codehaus.org/
The answer of Jonhatan is great and it has been very useful for me.
Just an upgrade:
if you use the version 2.x of Jackson (e.g. version 2.1) the class is com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, therefore the web.xml is:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>