I have JSON string that has nested objects with dynamic names that vary each time. For an instance:
{
"Objects": {
"dynamicName1": {
"name": "test"
},
"dynamicName2": {
"name": "test"
}
}
}
I was wondering how can you deserialize this string in APEX using wrapper classes?
I tried this:
public class masterobj
{ public childobj Objects;
}
public class childobj
{ public el dynamicName1;
public el dynamicName2;
}
public class el
{ public string name;
}
String s = '{"Objects":{"dynamicName1":{"name":"test"},"dynamicName2":{"name":"test"}}}';
masterobj mo = (masterobj)JSON.deserialize(s, masterobj.class);
which works well when you have declared the dynamic variable names in the class for each nested object.
The problem and the question is how can I make this work using a dynamic variable in the wrapper class. Because the object names will vary and also the number of the objects, I can't hard-code the names as they are different each time.
Any ideas?
You won't be able to deserialize a structure like that with the data binding features of the json parser, you'll need to use the streaming json parser to read it.
Use a Map:
public class masterobj
{
Map<String, el> Objects;
}
Related
I am getting a JSON in which it include list. Now I want to use only some property rather than all the properties.
JSON
{
"instance_pools" : [
{
"instance_pool_name":"pool1",
"node_type_id":"standard",
"azure_attributes":{
"availability":"on"
},
"instance_pool_id":"poool2345",
"status":{}
},
{
"instance_pool_name":"pool1",
"node_type_id":"normal",
"azure_attributes":{
"availability":"on"
},
"instance_pool_id":"poool2345",
"status":{}
},
{
"instance_pool_name":"pool1",
"node_type_id":"high",
"azure_attributes":{
"availability":"on"
},
"instance_pool_id":"poool2345",
"status":{}
}
]
}
Now at .Net side, I only want instance_pool_name and corresponding instance_pool_id.
I am new to .Net, Can somebody please help me with how to deserialize this complex JSON and use only some properties.
Thank you
You can create the classes as you want. You must not map all json properties into C# class' property. Therefore you can write the C# classes with the properties you need:
public class InstancePoolCollection
{
public InstancePool[] instance_pools{get;set;}
}
public class InstancePool
{
public string instance_pool_id{get;set;}
public string instance_pool_name{get;set;}
}
Deserializing using System.Text
var obj = JsonSerializer.Deserialize<InstancePoolCollection>(jsonObj);
Deserializing using Newtonsoft.Json
var obj = JsonConvert.DeserializeObject<InstancePoolCollection>(jsonObj);
Note: Your JSON was not conform. I have changed it.
I'm trying to set up a series of complex app settings in a separate settings.json file - I won't go into detail as to why...
So I have a JSON file which looks like this:
{
"Website": {
"Name": "Website Name",
"api_key": "----------",
"domain": "-----------"
},
"Pages": {
"Index": {
"Name": "Index",
"Widgets": {
"BestSellers": {
"Name": "BestSellers",
"Type": "ProductCollection",
"Data": {
"Limit": "8",
"Sort": {
"SortType": 3
},
"GetFullProducts": true,
"GroupVariations": false
}
}
}
}
}
}
The first section "Website" simply fetches string settings, all working fine.
The section section "Pages" is more complicated. I have classes that look like this:
public class PageSettings
{
public string Name { get; set; }
public Dictionary<String, Widget> Widgets { get; set; }
public class Widget
{
public string Name { get; set; }
public string Type { get; set; }
public Dictionary<String, object> Data { get; set; } // THIS IS THE PROPERTY THIS QUESTION IS ABOUT
}
}
I use this code to deserialise the above:
IConfigurationSection pagessection = root.GetSection("Pages");
if (pagessection.Exists())
{
_Pages = new Dictionary<String, PageSettings>();
pagessection.Bind(_Pages);
}
With the JSON File exactly as above, this will fail. For some reason, the nested Object Sort in the Data property cannot be deserialised as Object:
"Sort": {
"SortType": 3
}
If I take the above nested object out then all the code so far will work. However, there are use cases where I need that nested object.
I have tried using ExpandoObject which is very cool and clever, but because it expects KeyValuePairs, it then only serialises the nested object in Data, ignoring the simple properties Limit, GetFullroduct etc.
So what I need is a form of ExpandoObject which can also be ExpandoString or something?!
Alternatively... I need to be able to get the Data property from the settings.json file in String form and explicitly deserialise it using JsonConvert.Deserialize at the point of use, because at that point I can declare the proper class that it needs to be deserialised to, but i can't seem to find a way to get the IConfigurationSection code to get the value as a string, rather than a JSON object.
I can't find a solution to this, the nested object breaks everything I have tried.
The helpful comment from #Fei Han has helped a little in highlighting the flexibility of the JObject class, so the solution I have come to is this:
The complex class has to be stored as an HTML encoded string in the settings.json file:
"Data": "{"Limit": "8","GetFullProducts":true,"GroupVariations":true, "Sort": {"SortType": 3}}"
it has to be HTMLEncoded because it is the only way I can find to make the ConfigurationBuilder treat it as a string so that I can cast it correctly later.
The corresponding Class for this now has these properties:
public string ModelString { get; set; }
public Newtonsoft.Json.Linq.JObject Model
{
get
{
string s = ModelString.HtmlDecode();
if (s.EmptyStr())
{
return new JObject();
} else {
return JObject.Parse(s);
}
}
}
From this I am able to easily cast my Data to the eventually required class using .ToObject<MyObject>()
For some reason, this works. I am able to deserialise the string to a JObject in this method, but not directly using the Bind command on IConfigurationSection.
If anyone has any tips on why Bind won't do it, that'd be interesting!
I have the following POJO. It contains data about my airport.
class MyClass
{
#JsonProperty("AirportCode")
String airportCode;
#JsonProperty("AirportID")
Integer airportId;
}
The POJO objects are created using a JSON received from an API. the API sample output is like below.
[
{
"AirportCode": "BBA",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ",
"AirportID": 5325651,
}
]
My code is to function as follows
void func()
{
//Get JSON from API and convert to POJO
//Do some processing on the POJO
//Convert POJO into JSON and write to file
}
The file contents are as follows
[
{
"AirportCode": "BBA-IN",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ-USA",
"AirportID": 5325651,
}
]
I however require the output to be in camel case (like the POJO)
[
{
"airportCode": "BBA-IN",
"airportId": 4276802,
},
{
"airportCode": "SCQ-USA",
"airportId": 5325651,
}
]
Is there anyway I can get #JsonProperty to be honored only during deserialisation to POJO and not during serialization to JSON?
Have you tried to annotate getters and setters with different jsonProperty values? Directly it won't work but if you change the getter/setter name it should as both methods will be processed as if they belonged to different fields.
Something like this:
class MyClass {
String airportCode;
Integer airportId;
#JsonProperty("airportCode")
public String getAirportCodeForJson() {
return airportCode;
}
#JsonProperty("AirportCode")
public void setAirportCode(String airportCode) {
this.airportCode = airportCode;
}
#JsonProperty("airportID")
public Integer getAirportIdForJson() {
return airportId;
}
#JsonProperty("AirportID")
public void setAirportId(Integer airportId) {
this.airportId = airportId;
}
}
You could deserialize into a Dictionary and then camel-case all keys. Afterwards you'd be able to use it as intended.
I have a dynamic field, where the key is unknown and the value can be either a String or an Object.
Also, the field itself can be a map or just a single value.
How can I instruct Jackson to serialize/deserialize this field according to it's value type?
I'd also like to save my typed object to use in my code and not just use a map of Map<String, Object>.
public class MyPackage {
#JsonProperty("versions")
public Versions versions = new Versions();
#JsonAnySetter
public void add(String key, MyVersionObject value) {
versions.put(key, value);
}
#JsonAnyGetter
public Map<String, MyVersionObject> getMap() {
return versions;
}
}
Different JSONs that this field should accept:
{
"versions": {
"1.0": { ... MyVersionObject fields},
"2.0": { ... MyVersionObject fields},
"3.0": { ... MyVersionObject fields},
}
}
Or
{
"versions": {
"3.0": "latest"
}
}
Sounds like input is so irregular that you can't really define this dynamically. So leave type of value as Object; this will give you String or Map. And from that, convert yourself; either manually, or by using ObjectMapper.convertValue() method.
Following this example.
GET response is:
{
"singer":"Metallica",
"title":"Enter Sandman"
}
If more objects were included output should be like this:
[{
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]
I want to get the 'classname' written too. Something like this:
{"Track":[ {
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]}
Any simple ways to achieve this?
Looking forward to get data directly into Datatables from a JAX-RS Resteasy (Jackson) Server. Also trying to avoid DTO.
class TrackList
{
private List<Track> Track = new ArrayList<Track>();
// setter, getter
}
GET method
public TrackList getTrackInJSON() {
EDIT
GET method
public String getTrackInJSON() {
// ... create list of objects
return convertToString(objects);
}
utility method
static <T> String convertToString(List<T> list) throws IOException
{
final String json = new ObjectMapper().writeValueAsString(list);
return new StringBuilder()
.append("{\"")
.append(list.get(0).getClass().getSimpleName())
.append("\":")
.append(json)
.append("}")
.toString();
}