Runtime objects from JSONSchema in C# - c#

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.

Related

when i use json.net,how to deserialize String to .net Object by customized JsonConverter

For example, items in expressions may be a string, number, or an object. How to deserialize it to.NET object. I do not know how to define .NET class and do not know to implement JsonConverter.
{
"target": {
"propertyName": "AlertObjectInfo",
"valueType": "string"
},
"source": {
"operationName": "concat",
"expressions": [
"aa",
"bb",
2,
{
"operationName": "concat",
"expressions": [
"Name",
"Tom"
]
},
{
"operationName": "Add",
"expressions": [
3,
4
]
}
]
}
}
Convert your json payload to a corresponding C# object(s). You can do this in Visual Studio using the Paste Special option under the edit menu or as noted you can use an online tool to do the conversion.
This conversion will result in something like this based on your sample payload:
public class Root
{
[JsonPropertyName("target")]
public Target Target { get; set; }
[JsonPropertyName("source")]
public Source Source { get; set; }
}
public class Source
{
[JsonPropertyName("operationName")]
public string OperationName { get; set; }
[JsonPropertyName("expressions")]
public List<object> Expressions { get; set; }
}
public class Target
{
[JsonPropertyName("propertyName")]
public string PropertyName { get; set; }
[JsonPropertyName("valueType")]
public string ValueType { get; set; }
}
Then to deserialize the json into c# objects you'll make a call like the following.
System.Text.Json
JsonSerializer.Deserialize<Root>(json);
NewtonSoft
JsonConvert.DeserializeObject<Root>(json);
If you're using Newtonsoft you'll want to replace the [JsonPropertyName("target")] attributes with [JsonProperty("target")]

How to deserialize Json into an C# object and fill the components into a dictionary

{
"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)

convert objects model to json in c#?

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".

Elasticsearch Nest C# -- ElasticProperty Analyzer causing dynamic mapping

I am using ElasticProperty attributes to define my index type mapping. This works:
[ElasticProperty(Boost = 2)]
public string Title { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
public string ActivityType { get; set; }
I create my index and everything looks good (I'm just copying the affected props):
"properties": {
"activityType": {
"type": "string",
"index": "not_analyzed"
},
"title": {
"type": "string",
"boost": 2
}
}
BUT, when I drop the mapping, change the Analyzer and reindex watch what happens:
[ElasticProperty(Boost = 2, Analyzer = "keyword")]
public string Title { get; set; }
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
public string ActivityType { get; set; }
Result:
"properties": {
"activityType": {
"type": "string"
},
"title": {
"type": "string"
}
}
Can someone explain what's happening here? It seems that adding the Analyzer parameter forces the mapping to be generated dynamically. Why?
Make sure that on your application startup you're calling the following:
private readonly IElasticClient _client;
//initialize _client
_client.Map<YourTypeHere>(m => m.MapFromAttributes());
That code will apply any new mapping that you have.

How to deserialize json string in windows phone?

I just got my json response as a string.My json is given below,
"code": 0,
"message": "success",
"students": {
"details":{
"hjeke": {
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good",
},
"Second": {
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad",
}
}
}
Like hjeke and Second there are many key value pairs,how can i deserialize my json using Newtonsoft.json
Try to understand my solution in your previous question
How to deserialize json data in windows phone?
Your first JSON in that question was good and simple to use.
JSON, where field names are unique not convinient to deserialize. So, you got problems such as public class Hjeke and public class Second for each instance, when you use code generator.
Use JSON-structure with list of students:
"code": 0,
"message": "success",
"students": [
{
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good",
},
{
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad",
}]
is good and flexible structure. Using this, you don't need to parse not obvious fields like
"details":{
"hjeke": {
and so on.
And work with them using classes, from my previous answer. The main idea - you need list of objects. public List<StudentDetails> students. Then, all students objects deserialized in List, which is easy to use.
As everybody mentioned your json seems to be very unflexible, huh.
You can extract the data you are interested in.
So this is your model:
public class StudentDetails
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
And this is how you can extract it:
var jsonObj = JObject.Parse(str);
// get JSON result objects into a list
var results = jsonObj["students"]["details"].Children().Values();
// serialize JSON results into .NET objects
var details = new List<StudentDetails>();
foreach (JToken result in results)
{
var st = result.ToString();
var searchResult = JsonConvert.DeserializeObject<StudentDetails>(st);
details.Add(searchResult);
}
I'm using a newtonsoft.json library here.
Your Response string has some mistakes man, its not a valid json
just small modification to be done as below:
{
"code": 0,
"message": "success",
"students": {
"details": {
"hjeke": {
"id": "257633000000070001",
"name": "hjeke",
"percentage": 36,
"type": "Good"
},
"Second": {
"id": "257633000000073001",
"name": "Second",
"percentage": 4,
"type": "bad"
}
}
}
}
you can make out the difference
Now Follow these steps:
1.Go to this link Json to C#
2.place your Json string there and generate C# class object
3.Now create this class in your solution
4.Now deserialize As below
var DeserialisedObject = JsonConvert.DeserializeObject<Your Class>(YourJsonString);
First, create the classes:
public class Hjeke
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
public class Second
{
public string id { get; set; }
public string name { get; set; }
public int percentage { get; set; }
public string type { get; set; }
}
public class Details
{
public List<Hjeke> hjeke { get; set; }
public List<Second> Second { get; set; }
}
public class Students
{
public List<Details> details { get; set; }
}
public class RootObject
{
public int code { get; set; }
public string message { get; set; }
public List<Students> students { get; set; }
}
After that, use JSON.NET to deserialize:
var deserialized = JsonConvert.DeserializeObject<Class1>(YourStringHere);
Do you have any influence over the json response? Details should probably be a JSONArray in this case, not an object with a varying amount of properties, since I assume that's what you mean is the issue here.

Categories