Reputation: 367
I have a JSON notation which is a bit complex, especially since this is my first encounter with JSON. I used a third part tool to create C# classes for all relevant objects and arrays within the JSON but some of the internal objects are skipped. I then copied the the missing objects and created classes for them and used them accordingly.
I was able to parse and access most of the data but the most internal objects are not being parsed.
One of the hurdles for me is that the JSON contains more than one name/value pair with the same name i.e. "value".
JSON extract:
"controls": [
{
"value": "<p>The Governments current tobacco control strategy Creating a Tobacco-Free Generation: A Tobacco Control Strategy for Scotland sets out the Governments vision for a tobacco-free Scotland by 2034. Our contribution to this strategy is through</p>\n<ul>\n<li>prevention</li>\n<li>protection</li>\n<li>smoking cessation services.</li>\n</ul>\n<p>Tobacco smoking is a major risk factor for</p>\n<ul>\n<li>coronary heart disease</li>\n<li>stroke</li>\n<li>peripheral vascular disease</li>\n<li>many respiratory conditions including COPD</li>\n<li>a range of cancers </li>\n</ul>\n<p>and many other diseases and conditions. It is a leading cause of preventable ill health, premature death and disability.</p>\n<p>The Governments strategy outlines the importance of reducing health inequalities and the role that reducing smoking rates in the most deprived communities can have on this.</p>",
"editor": {
"alias": "rte"
}
},
{
"value": [
{
"callToAction": {
"value": [
{
"name": "Read Government’s tobacco control strategy (external website)",
"url": "http://www.gov.gov/Publications",
"target": "_blank",
"icon": "icon-link"
}
],
"dataTypeGuid": "5befdwe546fb12-3bc2-4f39-bab3-7328dfg345382afe65",
"editorAlias": "callToAction",
"editorName": "URL"
}
}
],
"editor": {
"alias": "callToAction"
},
"guid": "e3as34dc2831-c4e8-8671-038c-15634see45de7b71a"
},
{
"value": "<h2>Reducing tobacco-related inequalities </h2>\n<p>The European Regional Office of the World Health Organisation (WHO) has produced policy guidance focusing on reducing inequalities relating to tobacco smoking. The guidance looks at</p>\n<ul>\n<li>the widening socio-economic inequities in tobacco consumption</li>\n<li>how the use of tobacco contributes to health inequalities</li>\n<li>the complexity of smoking in terms of inequality</li>\n<li>how tobacco increases inequalities over the life course</li>\n<li>what tobacco policies need to consider to address inequalities.</li>\n</ul>\n<p>The guidance reports that</p>\n<ul>\n<li>A child born in a more socially deprived area of Scotland is more likely to be born into a family that smokes, is more likely to have a mother who smoked during her pregnancy and is more likely to be around smokers growing up. Children of smokers are more likely to start smoking themselves and continue to repeat the cycle of their own experience.</li>\n<li>People living in the most deprived areas are less likely to feel in control of their life, more likely to experience stress and mental health issues and be less aware of the harm of smoking and second hand smoke.</li>\n<li>When it comes to stopping smoking this group of smokers are more likely to smoke heavily and have a stronger nicotine dependence and therefore find it harder to stop. They are less likely to know where to get help, and have less encouragement and social support to quit.</li>\n<li>They are more likely to suffer financial hardship as a consequence of smoking related illness, more likely to live in poor housing and more likely to have other health problems made worse by smoking.</li>\n</ul>\n<p>You can read the full <a href=\"http://ec.europa.eu/health/social_determinants/key_documents/index_en.htm#anchor1\">‘Tobacco and inequities’ guidance</a> on the European Commission’s website (external website).</p>\n<p>Our tobacco control work is achieved by working in partnership with Government, local and national NHS Boards along with <a href=\"http://www.ashscotland.org.uk/\" target=\"_blank\">ASH Scotland</a> (external website) including the <a href=\"http://www.ashscotland.org.uk/alliances\" target=\"_blank\"> Tobacco-free Alliance</a> (external website).</p>\n<h2>Getting help with local tobacco planning</h2>\n<p>To help with local planning, Public Health Observatory () have created local tobacco control profiles which include indicators on </p>\n<ul>\n<li>adult smoking prevalence</li>\n<li>smoking during and post-pregnancy</li>\n<li>smoking cessation</li>\n<li>smoking related death and disease</li>\n<li>tobacco sales.</li>\n</ul>",
"editor": {
"alias": "rte"
}
},
{
"value": [
{
"callToAction": {
"value": [
{
"name": "Visit the TEST online profiles tool (external website)",
"url": "http://www.TEST.org.uk/comparative-health/profiles/online-profiles-tool",
"target": "_blank",
"icon": "icon-link"
}
],
"dataTypeGuid": "5bs3a232-3bc2-4f39-bab3-73283ewr82afe65",
"editorAlias": "callToAction",
"editorName": "URL"
}
}
],
"editor": {
"alias": "callToAction"
},
"guid": "25ese341-4c05-0c11-e643-3f3aderd84e8e3"
}
]
I can only get the first object ({"value":"<p>The ....", "editor":{...}}
but can't access the second object that follows and which has the callToAction
Any help will be greatly appreciated.
I can share code if needed. I am using C# & Newtonsoft.Json.
@jeff mercado, here is the code: The Classes:
using Newtonsoft.Json;
namespace CallToAction.Models
{
public class Control
{
[JsonProperty("value")]
public Value[] Value { get; set; }
[JsonProperty("editor")]
public Editor Editor { get; set; }
}
}
using Newtonsoft.Json;
namespace CallToAction.Models
{
public class Value
{
[JsonProperty("callToAction")]
public CallToAction CallToAction { get; set; }
}
}
using Newtonsoft.Json;
namespace CallToAction.Models
{
public class CallToAction
{
[JsonProperty("value")]
public Value2[] Value { get; set; }
[JsonProperty("dataTypeGuid")]
public string DataTypeGuid { get; set; }
[JsonProperty("editorAlias")]
public string EditorAlias { get; set; }
[JsonProperty("editorName")]
public string EditorName { get; set; }
}
}
And here is the line of code the is parsing the Json from a file on my local machine.
Control values = JsonConvert.DeserializeObject<Control>(System.IO.File.ReadAllText(@"C:\\Folder\\CallToAction\\CallToAction\\Content\\Value.json"));
Upvotes: 0
Views: 81
Reputation: 116731
It appears your "controls": [{...}, {...}]
array is polymorphic: it can contain various types of control, the type being indicated by the nested token "editor.alias"
. You can model this as a class hierarchy, then deserialize it along the lines of Deserializing polymorphic json classes without type information using json.net.
Your polymorphic class hierarchy can be modeled as follows:
[JsonConverter(typeof(ControlBaseConverter))]
public abstract class ControlBase
{
public Editor editor { get; set; }
}
public class Editor
{
public string alias { get; set; }
}
public class CallToActionValue
{
public string name { get; set; }
public string url { get; set; }
public string target { get; set; }
public string icon { get; set; }
}
public class CallToAction
{
public List<CallToActionValue> value { get; set; }
public string dataTypeGuid { get; set; }
public string editorAlias { get; set; }
public string editorName { get; set; }
}
public class CallToActionControlValue
{
public CallToAction callToAction { get; set; }
}
[JsonConverter(typeof(NoConverter))]
public class CallToActionControl : ControlBase
{
public List<CallToActionControlValue> value { get; set; }
public string guid { get; set; }
}
[JsonConverter(typeof(NoConverter))]
public class RteControl : ControlBase
{
public string value { get; set; }
}
public class RootObject
{
public List<ControlBase> controls { get; set; }
}
Using the following custom JSON converters:
class ControlBaseConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ControlBase);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
var editorValue = (string)obj.SelectToken("editor.alias");
Type type;
if (string.Equals(editorValue, "rte", StringComparison.OrdinalIgnoreCase))
{
type = typeof(RteControl);
}
else if (string.Equals(editorValue, "callToAction", StringComparison.OrdinalIgnoreCase))
{
type = typeof(CallToActionControl);
}
else
{
throw new JsonSerializationException("Unknown type of Control: " + editorValue);
}
return obj.ToObject(type, serializer);
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class NoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return false;
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then do:
var root = JsonConvert.DeserializeObject<RootObject>(jsonString);
Adding [JsonConverter(typeof(NoConverter))]
to the derived classes prevents an infinite recursion when the base type converter attempts to deserialize a derived type, as otherwise the [JsonConverter(typeof(ControlBaseConverter))]
attribute is inherited.
Sample fiddle.
Upvotes: 1