I have a object model I want to show as a json string, for example:
public class SectionD
{
public string InsertID { get; set; }
public int CaseReference { get; set; }
public string AdditionalInfo { get; set; }
public DateTime CreationDate { get; set; }
}
and I want to present this as a json object, like so:
{
"class": "SectionD",
"parameters": [
{
"key": "InsertID",
"type": "string"
},
{
"key": "CaseReference",
"type": "int"
},
{
"key": "AdditionalInfo",
"type": "string"
},
{
"key": "CreationDate",
"type": "DateTime"
}
]
}
The data is being stored as a json string in a database, and I want to provide a list of fields and types to someone who would be making database views on that data.
google provides a lot of hits for querying the contents on the model, but I can't find anything for looking at the object itself.
Thanks
How about something simple like:
public class ReflectedPropertyInfo
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
}
public class ReflectJson
{
public static string ReflectIntoJson<T>() where T : class
{
var type = typeof(T);
var className = type.Name;
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var propertyList = new List<ReflectedPropertyInfo>();
foreach (var prop in props)
{
propertyList.Add(new ReflectedPropertyInfo{Key =prop.Name, Type =prop.PropertyType.Name});
}
var result = JsonConvert.SerializeObject(new {#class = className, parameters = propertyList}, Formatting.Indented);
return result;
}
}
It uses Reflection, as suggested by #dbc. After getting the type name, it gets a collection of properties and then builds up a anonymous type containing the information in the correct format, and then serializes it. The result looks like:
{
"class": "SectionD",
"parameters": [
{
"key": "InsertID",
"type": "String"
},
{
"key": "CaseReference",
"type": "Int32"
},
{
"key": "AdditionalInfo",
"type": "String"
},
{
"key": "CreationDate",
"type": "DateTime"
}
]
}
The only difference (that I see) is that it uses the actual "Int32" as the type name for integers, rather than the C# alias "int".
Related
{
"success": true,
"data": [{
"type": "Employee",
"attributes": {
"id": {
"label": "ID",
"value": 8556527,
"type": "integer",
"universal_id": "id"
},
"email": {
"label": "Email",
"value": "exapmle#gmail.com",
"type": "standard",
"universal_id": "email"
},
"dynamic_2682839": {
"label": "DATEV Personalnumber",
"value": "31604",
"type": "standard",
"universal_id": "staff_number"
}
}
},
public class Id
{
[JsonPropertyName("label")]
public string Label { get; set; }
[JsonPropertyName("value")]
public int Value { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("universal_id")]
public string UniversalId { get; set; }
}
public class Email
{
[JsonPropertyName("label")]
public string Label { get; set; }
[JsonPropertyName("value")]
public string Value { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("universal_id")]
public string UniversalId { get; set; }
}
public class Dynamic2682839
{
[JsonPropertyName("label")]
public string Label { get; set; }
[JsonPropertyName("value")]
public string Value { get; set; }
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("universal_id")]
public string UniversalId { get; set; }
}
public class Attributes
{
[JsonPropertyName("id")]
public Id Id { get; set; }
[JsonPropertyName("email")]
public Email Email { get; set; }
[JsonPropertyName("dynamic_2682839")]
public Dynamic2682839 Dynamic2682839 { get; set; }
}
public class Datum
{
[JsonPropertyName("type")]
public string Type { get; set; }
[JsonPropertyName("attributes")]
public Attributes Attributes { get; set; }
}
public class Root
{
[JsonPropertyName("success")]
public bool Success { get; set; }
[JsonPropertyName("data")]
public List<Datum> Data { get; set; }
}
I know it is probably are very simple solution behind it but i cant get to it. My Problem is that I want to deserialize this type of data into an object for example. This json file goes on forever but the only thing I'm interested in are the "ID" and the "Personalnumber" so I can create a dictionary for further processing with my code.
I mean I could just sit there for two days and add everything to the dictionary but first of all it would drive me crazy and second of all the dictionary should add new members automatically to the dictionary and shouldn't be static. I already converted the Json into class objects and downloaded Newtonsoft for my IDE and with this comand:
Id ids = JsonConvert.DeserializeObject<Id>(json);
Console.WriteLine(ids.Value);
I try to get all the values from the ID´s but all i get is a 0.
The thing is i tested everything so far and it works perfectly but my deserialization dont work as planned.
If anyone could help a newbie I would appreciate it.
This looks wrong
Id ids = JsonConvert.DeserializeObject<Id>(json);
You should probably be deserializing to Root instead.
Root root = JsonConvert.DeserializeObject<Root>(json);
From there you can get all the IDs with
List<Id> ids = root.Data.Select(datum => datum.Attributes.Id);
This json file goes on forever but the only thing I'm interested in are the "ID" and the "Personalnumber" ... the dictionary should add new members automatically ... and shouldn't be static.
Phuzi's answer seizes onto the wrong thing, giving you the contents of the "id" object, which does get you the ID, but ignores the Personalnumber..
..which also looks like it might not always be in a dynamic_2682839
We need a different strategy for getting the data; we can look to see if the label contains ID or Personalnumber but we could really do with the serializes deser'ing this data to a common set of objects, not a different Type (with the same properties) each time
I don't know if you used QuickType.IO for generating your JSON classes but, if you didn't, I would recommend it; it's a bit more sophisticated than other generators. You can trick it into helping you more by modifying your JSON..
Take this:
{
"success": true,
"data": [
{
"type": "Employee",
"attributes": {
"id": {
"label": "ID",
"value": 8556527,
"type": "integer",
"universal_id": "id"
},
"email": {
"label": "Email",
"value": "exapmle#gmail.com",
"type": "standard",
"universal_id": "email"
},
"dynamic_2682839": {
"label": "DATEV Personalnumber",
"value": "31604",
"type": "standard",
"universal_id": "staff_number"
}
}
}
]
}
And make it into this (put some more objects in "attributes", and make the keys of the dictionary into sequential numbers):
{
"success": true,
"data": [{
"type": "Employee",
"attributes": {
"1": {
"label": "ID",
"value": 8556527,
"type": "integer",
"universal_id": "id"
},
"2": {
"label": "Email",
"value": "exapmle#gmail.com",
"type": "standard",
"universal_id": "email"
},
"3": {
"label": "DATEV Personalnumber",
"value": "31604",
"type": "standard",
"universal_id": "staff_number"
},
"4": {
"label": "DATEV Personalnumber",
"value": "31604",
"type": "standard",
"universal_id": "staff_number"
},
"5": {
"label": "DATEV Personalnumber",
"value": "31604",
"type": "standard",
"universal_id": "staff_number"
}
}
}]
}
which will help QT see more easily that a Dictionary<string, Attribute> is suitable for "attributes", and a custom converter is needed for the varying type of "value"...
Otherwise you'll just get multiple identical classes (your Id, Email and Dynamic_123 classes) and a property for every member of "attributes" even though they're all the same:
You thus get these classes out of QT:
namespace SomeNamespace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class SomeRoot
{
[JsonProperty("success")]
public bool Success { get; set; }
[JsonProperty("data")]
public Datum[] Data { get; set; }
}
public partial class Datum
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public Dictionary<string, Attribute> Attributes { get; set; }
}
public partial class Attribute
{
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("value")]
public Value Value { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("universal_id")]
public string UniversalId { get; set; }
}
public partial struct Value
{
public long? Integer;
public string String;
public static implicit operator Value(long Integer) => new Value { Integer = Integer };
public static implicit operator Value(string String) => new Value { String = String };
}
public partial class SomeRoot
{
public static SomeRoot FromJson(string json) => JsonConvert.DeserializeObject<SomeRoot>(json, SomeNamespace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this SomeRoot self) => JsonConvert.SerializeObject(self, SomeNamespace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
ValueConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class ValueConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Value) || t == typeof(Value?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Integer:
var integerValue = serializer.Deserialize<long>(reader);
return new Value { Integer = integerValue };
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new Value { String = stringValue };
}
throw new Exception("Cannot unmarshal type Value");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (Value)untypedValue;
if (value.Integer != null)
{
serializer.Serialize(writer, value.Integer.Value);
return;
}
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
throw new Exception("Cannot marshal type Value");
}
public static readonly ValueConverter Singleton = new ValueConverter();
}
}
and you can get your ID/Personalnumber attributes like this:
var someRoot = SomeRoot.FromJson(File.ReadAllText("a.json"));
var attribs = someRoot.Data.SelectMany(d => d.Attributes.Where(a => a.Value.Label == "ID" || a.Value.Label.Contains("Personalnumber"))).ToArray();
or some other LINQ of your choosing on the Dictionary at the path "data"."attributes" - here I've chosen to SelectMany; if Data had 10 elements, and each element had 4 Attributes, 2 of which were ID or Personalnumber, you'd get a single array of 20 long, of the ID/Personalnumber Attributes. You might not want to SelectMany, if you want to keep them together (they're only loosely related by ordering in the array after a SelectMany)
I have this JSON:
[
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 1",
"Values": [
"Acc 1"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "1",
"Values": [
"1"
]
}
}
],
"Name": "account",
"Id": "1"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 2",
"Values": [
"Acc 2"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "2",
"Values": [
"2"
]
}
}
],
"Name": "account",
"Id": "2"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 3",
"Values": [
"Acc 3"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "3",
"Values": [
"3"
]
}
}
],
"Name": "account",
"Id": "2"
}
]
And I have these classes:
public class RetrieveMultipleResponse
{
public List<Attribute> Attributes { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}
public class Value
{
[JsonProperty("Value")]
public string value { get; set; }
public List<string> Values { get; set; }
}
public class Attribute
{
public string Key { get; set; }
public Value Value { get; set; }
}
I am trying to deserialize the above JSON using the code below:
var objResponse1 = JsonConvert.DeserializeObject<RetrieveMultipleResponse>(JsonStr);
but I am getting this error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'test.Model.RetrieveMultipleResponse' because the type requires a JSON
object (e.g. {"name":"value"}) to deserialize correctly. To fix this
error either change the JSON to a JSON object (e.g. {"name":"value"})
or change the deserialized type to an array or a type that implements
a collection interface (e.g. ICollection, IList) like List that can
be deserialized from a JSON array. JsonArrayAttribute can also be
added to the type to force it to deserialize from a JSON array. Path
'', line 1, position 1.
Your json string is wrapped within square brackets ([]), hence it is interpreted as array instead of single RetrieveMultipleResponse object. Therefore, you need to deserialize it to type collection of RetrieveMultipleResponse, for example :
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
If one wants to support Generics (in an extension method) this is the pattern...
public static List<T> Deserialize<T>(this string SerializedJSONString)
{
var stuff = JsonConvert.DeserializeObject<List<T>>(SerializedJSONString);
return stuff;
}
It is used like this:
var rc = new MyHttpClient(URL);
//This response is the JSON Array (see posts above)
var response = rc.SendRequest();
var data = response.Deserialize<MyClassType>();
MyClassType looks like this (must match name value pairs of JSON array)
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class MyClassType
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "Description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "Manager")]
public string Manager { get; set; }
[JsonProperty(PropertyName = "LastUpdate")]
public DateTime LastUpdate { get; set; }
}
Use NUGET to download Newtonsoft.Json add a reference where needed...
using Newtonsoft.Json;
Can't add a comment to the solution but that didn't work for me. The solution that worked for me was to use:
var des = (MyClass)Newtonsoft.Json.JsonConvert.DeserializeObject(response, typeof(MyClass));
return des.data.Count.ToString();
Deserializing JSON array into strongly typed .NET object
Use this, FrontData is JSON string:
var objResponse1 = JsonConvert.DeserializeObject<List<DataTransfer>>(FrontData);
and extract list:
var a = objResponse1[0];
var b = a.CustomerData;
To extract the first element (Key) try this method and it will be the same for the others :
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync("Your URL"))
{
var apiResponse = await response.Content.ReadAsStringAsync();
var list = JObject.Parse(apiResponse)["Attributes"].Select(el => new { Key= (string)el["Key"] }).ToList();
var Keys= list.Select(p => p.Key).ToList();
}
}
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
worked!
You can validate an object using a JSON Schema.
You can generate dynamic objects at runtime with dynamic and expanddo
What I would like to do, is generate objects at runtime from JSON Schemas, that can then be populated as necessary.
If this seems weird, the reason is the JSON Schema will define a template to be populated from another source, but the system allows you to create new templates.
Example as requested:
Parent Object
public class Parent
{
public Guid Id { get; set; }
public string Name { get; set; }
public int IntValue { get; set; }
public decimal DecimalValue { get; set; }
public double DoubleValue { get; set; }
public float FloatValue { get; set; }
public DateTime DateTimeValue { get; set; }
[EnumDataType(typeof(EnumData))]
public string EnumValue { get; set; }
public List<Child> children { get; set; }
}
public enum EnumData
{
Alpha,
Beta,
Charlie
}
Child Object
public class Child
{
public Guid ParentId { get; set; }
public string ChildName { get; set; }
public int ChildInt { get; set; }
}
Resulting JSON Schema
{
"type": "object",
"properties": {
"Id": {
"type": "string"
},
"Name": {
"type": [
"string",
"null"
]
},
"IntValue": {
"type": "integer"
},
"DecimalValue": {
"type": "number"
},
"DoubleValue": {
"type": "number"
},
"FloatValue": {
"type": "number"
},
"DateTimeValue": {
"type": "string",
"format": "date-time"
},
"EnumValue": {
"type": [
"string",
"null"
],
"enum": [
"Alpha",
"Beta",
"Charlie"
]
}
},
"required": [
"Id",
"Name",
"IntValue",
"DecimalValue",
"DoubleValue",
"FloatValue",
"DateTimeValue",
"EnumValue"
]
}
The schema is a definition of an object. If this definitions was created by a user choosing what properties they want, they type, acceptable values etc, then a schema could be generated.
If you could then create an instance of an object from this schema, the properties could be populated.
The point is the class won't be known at coding time.
The reason for this is we have a huge generic set of data that we need to create smaller sets from, that the user defines what these sets will be from inside the application. The definition they create can be stored and used again.
You can do this with my new library, JsonSchema.Net.Generation.
var schema = new JsonSchemaBuilder().FromType(myType);
You can read more about it in the docs.
I think #gregsdennis got the library right. If you look at the documentation it can be done.
The NJsonSchema.CodeGeneration can be used to generate C# or TypeScript code from a JSON schema:
var generator = new CSharpGenerator(schema);
var file = generator.GenerateFile();
The file variable now contains the C# code for all the classes defined in the JSON schema.
I'm currently working on a webservice, and I have this behavior that I haven't encountered until today. Here's the class I'm returning:
public class Block
{
public int order { get; set; }
public string title { get; set; }
public Dictionary<string, object> attributes { get; set; }
}
attributes can contain any kind of value: simple type, object, array, etc.
And when I return a Block object through my webservice, here's what I get:
{
"order": 1,
"attributes": [
{
"Key": "key1",
"Value": "value1"
},
{
"Key": "key2",
"Value": "value2"
}
],
"title": "Title"
}
Does anyone know why I'm not simply getting a "key1": "value1" output?
First of all your JSON is not valid, please be aware there are no , after "order": 1 this line.
After this correction you can change your class structure like this
public class Attribute
{
public string Key { get; set; }
public object Value { get; set; }
}
public class Block
{
public int order { get; set; }
public List<Attribute> attributes { get; set; }
public string title { get; set; }
}
this way you will be able to deserialize your JSON, I used simply this website https://json2csharp.com/ for converting your JSON into C# class
As for usage you can do .FirstOrDefault(x=>x.Key=="key1") to get whichever data you want, or if you will process all the list one by one you can simply do attributes.Count
When I do
var x = new Class
{
order = 1,
title = "Title",
attributes = new Dictionary<string, object>
{
{ "key1", "value1" },
{ "key2", "value2" }
}
};
var json = JsonConvert.SerializeObject(x);
Console.WriteLine(json);
I get
{"order":1,"title":"Title","attributes":{"key1":"value1","key2":"value2"}}
Do you have code that serializes your data or does ASP.NET do it for you?
I have this JSON:
[
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 1",
"Values": [
"Acc 1"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "1",
"Values": [
"1"
]
}
}
],
"Name": "account",
"Id": "1"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 2",
"Values": [
"Acc 2"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "2",
"Values": [
"2"
]
}
}
],
"Name": "account",
"Id": "2"
},
{
"Attributes": [
{
"Key": "Name",
"Value": {
"Value": "Acc 3",
"Values": [
"Acc 3"
]
}
},
{
"Key": "Id",
"Value": {
"Value": "3",
"Values": [
"3"
]
}
}
],
"Name": "account",
"Id": "2"
}
]
And I have these classes:
public class RetrieveMultipleResponse
{
public List<Attribute> Attributes { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}
public class Value
{
[JsonProperty("Value")]
public string value { get; set; }
public List<string> Values { get; set; }
}
public class Attribute
{
public string Key { get; set; }
public Value Value { get; set; }
}
I am trying to deserialize the above JSON using the code below:
var objResponse1 = JsonConvert.DeserializeObject<RetrieveMultipleResponse>(JsonStr);
but I am getting this error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'test.Model.RetrieveMultipleResponse' because the type requires a JSON
object (e.g. {"name":"value"}) to deserialize correctly. To fix this
error either change the JSON to a JSON object (e.g. {"name":"value"})
or change the deserialized type to an array or a type that implements
a collection interface (e.g. ICollection, IList) like List that can
be deserialized from a JSON array. JsonArrayAttribute can also be
added to the type to force it to deserialize from a JSON array. Path
'', line 1, position 1.
Your json string is wrapped within square brackets ([]), hence it is interpreted as array instead of single RetrieveMultipleResponse object. Therefore, you need to deserialize it to type collection of RetrieveMultipleResponse, for example :
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
If one wants to support Generics (in an extension method) this is the pattern...
public static List<T> Deserialize<T>(this string SerializedJSONString)
{
var stuff = JsonConvert.DeserializeObject<List<T>>(SerializedJSONString);
return stuff;
}
It is used like this:
var rc = new MyHttpClient(URL);
//This response is the JSON Array (see posts above)
var response = rc.SendRequest();
var data = response.Deserialize<MyClassType>();
MyClassType looks like this (must match name value pairs of JSON array)
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class MyClassType
{
[JsonProperty(PropertyName = "Id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "Description")]
public string Description { get; set; }
[JsonProperty(PropertyName = "Manager")]
public string Manager { get; set; }
[JsonProperty(PropertyName = "LastUpdate")]
public DateTime LastUpdate { get; set; }
}
Use NUGET to download Newtonsoft.Json add a reference where needed...
using Newtonsoft.Json;
Can't add a comment to the solution but that didn't work for me. The solution that worked for me was to use:
var des = (MyClass)Newtonsoft.Json.JsonConvert.DeserializeObject(response, typeof(MyClass));
return des.data.Count.ToString();
Deserializing JSON array into strongly typed .NET object
Use this, FrontData is JSON string:
var objResponse1 = JsonConvert.DeserializeObject<List<DataTransfer>>(FrontData);
and extract list:
var a = objResponse1[0];
var b = a.CustomerData;
To extract the first element (Key) try this method and it will be the same for the others :
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.GetAsync("Your URL"))
{
var apiResponse = await response.Content.ReadAsStringAsync();
var list = JObject.Parse(apiResponse)["Attributes"].Select(el => new { Key= (string)el["Key"] }).ToList();
var Keys= list.Select(p => p.Key).ToList();
}
}
var objResponse1 =
JsonConvert.DeserializeObject<List<RetrieveMultipleResponse>>(JsonStr);
worked!