I am having trouble understanding NJsonSchema's take on the following schema and the classes generated. I cannot change the schema as this is defined by someone else. The schema posted below is simplified to demonstrate the problem (link to the entire schema). I would like to create a set of C# classes that can output a valid file following the schema. However, the NJsonSchema code generator just fills in generic objects and does not populate the fields and properties described by the schema.
The generator code:
var schema = JsonSchema.FromFileAsync(#"../../../SchemaSnippets.json");
CSharpGeneratorSettings set = new CSharpGeneratorSettings();
set.Namespace = "EPJSON";
set.SchemaType = SchemaType.JsonSchema;
set.GenerateDataAnnotations = true;
set.AnyType = "object";
set.ClassStyle = CSharpClassStyle.Poco;
set.HandleReferences = false;
var generator = new CSharpGenerator(schema.Result, set);
var file = generator.GenerateFile();
File.WriteAllText(#"../../../SchemaSnippetsCode.cs", file);
The Schema:
{
"$schema": "https://json-schema.org/draft-07/schema#",
"properties": {
"Version": {
"patternProperties": {
".*": {
"type": "object",
"properties": {
"version_identifier": {
"type": "string",
"default": "22.2"
}
}
}
},
"group": "Simulation Parameters",
"legacy_idd": {
"field_info": {
"version_identifier": {
"field_name": "Version Identifier",
"field_type": "a"
}
},
"fields": [
"version_identifier"
],
"alphas": {
"fields": [
"version_identifier"
]
},
"numerics": {
"fields": []
}
},
"type": "object",
"maxProperties": 1,
"memo": "Specifies the EnergyPlus version of the IDF file.",
"format": "singleLine"
}
}
}
The generated class output looks like this:
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.8.0.0 (Newtonsoft.Json v9.0.0.0)")]
[Newtonsoft.Json.JsonProperty("Version", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public System.Collections.Generic.IDictionary<string, object> Version { get; set; }
private System.Collections.Generic.IDictionary<string, object> _additionalProperties;
[Newtonsoft.Json.JsonExtensionData]
public System.Collections.Generic.IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, object>()); }
set { _additionalProperties = value; }
}
I was expecting/hoping for something along these lines:
public System.Collections.Generic.IDictionary<string, VersionObject> Version { get; set; }
class VersionObject
{
string version_identifier { get; set; } = "22.2";
}
Related
I got really confusing with json ( I still learning about json ).
the JSON I want to:
{ "entityMap": {
"0": {
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
},
"1": {
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
},
"2": {
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
},
"3": {
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
}
}
}
Is that considered bad json? And how to naming json with number like that?
This by far what I got.
{
"entityMap": [
{
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
},
{
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
},
{
"type": "LINK",
"mutability": "MUTABLE",
"data": {
"url": "https://stackoverflow.com/"
}
}
]
}
this my class
public class editorRawTest
{
public List<entityMapItem> entityMap { get; set; }
}
public class entityMapItem
{
public string type { get; set; }
public string mutability { get; set; }
public entityMapItemData data { get; set; }
}
public class entityMapItemData
{
public string url { get; set; }
}
my execute code:
var map = new List<entityMapItem>();
var mapitem = new entityMapItem() { type = "LINK", mutability = "MUTABLE", data = new entityMapItemData() { url = "https://stackoverflow.com/" } };
map.Add(mapitem);
map.Add(mapitem);
map.Add(mapitem);
editorRawTest bc = new editorRawTest() { entityMap = map };
string JSONresult = JsonConvert.SerializeObject(bc);
string path = #"jsonmapdata.json";
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(JSONresult.ToString());
tw.Close();
}
Searching through google and stackoverflow with no luck.
Any clue or help will be appreciated.
Thank you.
To get the first JSON, you would need to replace your List<entityMapItem> with a Dictionary<string, entityMapItem>, like this:
public class editorRawTest
{
public Dictionary<string, entityMapItem> entityMap { get; set; }
}
Then you would need to fill it like this:
var map = new Dictionary<string, entityMapItem>();
var mapitem = new entityMapItem() { type = "LINK", mutability = "MUTABLE", data = new entityMapItemData() { url = "https://stackoverflow.com/" } };
for (int i = 0; i < 4; i++)
{
map.Add(i.ToString(), mapitem);
}
But I would discourage you from doing that unless you have to. What you have now (the second JSON with the list) is much easier to work with. If you have a choice between the two, it is better to choose the second approach. See Using json key to store value, is it a good approach? for why.
I am really new to c# language programming and
I have a JSON string:
{
"Type": "Name",
"parameters": [
{
"A": {
"type": "string",
"defaultValue": "key"
},
"B": {
"type": "string",
"defaultValue": "key"
},
"C": {
"type": "string",
"defaultValue": "key"
},
"D": {
"type": "string",
"defaultValue": "autogenerated"
},
"E": {
"type": "string",
"defaultValue": "autogenerated"
},
"F": {
"type": "dropdown",
"dropDownItems": [
"true",
"false"
],
"defaultValue": "false"
}
}
]
}
and I want to output the JSON array parameters but without "A", "B" and "C".
This JSON File is always changing but it always have this "A", "B" and "C".
Among with the answer of Thierry Prost
namespace Testedouble
{
class Program
{
static void Main(string[] args)
{
var jsonString = #"{
'Type': 'Name',
'parameters': [
{
'A': {
'type': 'string',
'defaultValue': 'key'
},
'B': {
'type': 'string',
'defaultValue': 'key'
},
'C': {
'type': 'string',
'defaultValue': 'key'
},
'D': {
'type': 'string',
'defaultValue': 'autogenerated'
},
'E': {
'type': 'string',
'defaultValue': 'autogenerated'
},
'F': {
'type': 'dropdown',
'dropDownItems': [
'true',
'false'
],
'defaultValue': 'false'
}
}
]
}";
var values = JsonConvert.DeserializeObject<Foo>(jsonString);
foreach (var key in new string[] { "A", "B", "C" })
{
foreach (var item in values.parameters)
{
item.Remove(key);
}
}
Console.WriteLine(JsonConvert.SerializeObject(values));
}
public class Foo
{
public string Type { get; set; }
public List<Dictionary<string, object>> Parameters { get; set; }
}
}
}
I made a small console application that shows the desired result. The flow is as follows:
We create the C# classes we need for the given JSON: DropdownInfo, ParameterInfo, ParameterBase, Base. I made them several so you can better extend them as you need.
We Deserialize the Object and then modify it the way we want.
var itemsToRemove = new string[] { "A", "B", "C" };
Here we add all the elements, which we don't want to be in the output. In our case, we remove A, B, C
- Serialize back the Object to JSON. We use Formatting.Indented, so the result looks better (beautifier, human readable)
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text;
namespace JsonExercise
{
public class JsonExercise
{
public static void Main(string[] args)
{
var sb = new StringBuilder();
var line = string.Empty;
while (!string.IsNullOrWhiteSpace((line = Console.ReadLine())))
{
sb.AppendLine(line);
}
var json = sb.ToString().Trim();
var inputObj = JsonConvert.DeserializeObject<Base>(json);
var resultObj = new
{
Type = inputObj.Type,
Parameters = new List<object>()
};
Console.WriteLine("--------------------------------");
//Here we can give all the Properties, which will be skipped!
var itemsToRemove = new string[] { "A", "B", "C" };
var propertiesToAdd = new Dictionary<string, object>();
foreach (var propertyInfo in typeof(ParameterBase).GetProperties())
{
if (!itemsToRemove.Contains(propertyInfo.Name))
{
var propertyValue = (inputObj.Parameters[0]).GetType().GetProperty(propertyInfo.Name).GetValue(inputObj.Parameters[0]);
propertiesToAdd.Add($"{propertyInfo.Name}", propertyValue);
}
}
var objToAdd = GetDynamicObject(propertiesToAdd);
resultObj.Parameters.Add(objToAdd);
Console.WriteLine("Serializing Object");
Console.WriteLine(JsonConvert.SerializeObject(resultObj, Formatting.Indented));
}
public static dynamic GetDynamicObject(Dictionary<string, object> properties)
{
return new MyDynObject(properties);
}
}
public sealed class MyDynObject : DynamicObject
{
private readonly Dictionary<string, object> _properties;
public MyDynObject(Dictionary<string, object> properties)
{
_properties = properties;
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return _properties.Keys;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (_properties.ContainsKey(binder.Name))
{
result = _properties[binder.Name];
return true;
}
else
{
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_properties.ContainsKey(binder.Name))
{
_properties[binder.Name] = value;
return true;
}
else
{
return false;
}
}
}
public class Base
{
public string Type { get; set; }
public ParameterBase[] Parameters { get; set; }
}
public class ParameterBase
{
public ParameterInfo A { get; set; }
public ParameterInfo B { get; set; }
public ParameterInfo C { get; set; }
public ParameterInfo D { get; set; }
public ParameterInfo E { get; set; }
public DropdownInfo F { get; set; }
}
public class ParameterInfo
{
public string Type { get; set; }
public string DefaultValue { get; set; }
}
public class DropdownInfo
{
public string Type { get; set; }
public string DefaultValue { get; set; }
public string[] DropDownItems { get; set; }
}
}
The first part of the code with the StringBuilder class is just to read the input(The given JSON).
I will give sample input and output JSON data.
--INPUT--
{
"Type": "Name",
"parameters": [
{
"A": {
"type": "string",
"defaultValue": "key"
},
"B": {
"type": "string",
"defaultValue": "key"
},
"C": {
"type": "string",
"defaultValue": "key"
},
"D": {
"type": "string",
"defaultValue": "autogenerated"
},
"E": {
"type": "string",
"defaultValue": "autogenerated"
},
"F": {
"type": "dropdown",
"dropDownItems": [
"true",
"false"
],
"defaultValue": "false"
}
}
]
}
--OUTPUT--
{
"Type": "Name",
"Parameters": [
{
"D": {
"Type": "string",
"DefaultValue": "autogenerated"
},
"E": {
"Type": "string",
"DefaultValue": "autogenerated"
},
"F": {
"Type": "dropdown",
"DefaultValue": "false",
"DropDownItems": [
"true",
"false"
]
}
}
]
}
Edit: Changed the code after #João Paulo Amorim comment. I tested the code it works fine, use it freely.
Shout out to João Paulo Amorim and his answer. Looks smoother.
PS. My first answer on StackOverFlow \o/
With Newtonsoft.Json package:
using System;
using Newtonsoft.Json;
using System.IO;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
//File with json
string jsontext = File.ReadAllText("json.json");
dynamic json = JsonConvert.DeserializeObject(jsontext);
foreach(var parameter in json.parameters)
{
Console.WriteLine(parameter.D);
Console.WriteLine(parameter.E);
Console.WriteLine(parameter.F);
}
Console.ReadLine();
}
}
}
Using newtonsoft library
Working Fiddle added
public class Foo
{
public string Type { get; set; }
[JsonProperty("parameters")]
public List<Dictionary<string, object>> Parameters { get; set; }
[JsonProperty("defaultValue")]
public string DefaultValue { get; set; }
}
var values = JsonConvert.DeserializeObject<Foo>(jsonStr);
values.Parameters = values
.Parameters
.Select(
dic => dic
.Where(kvp => new string[] { "A", "B", "C" }.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
.ToList();
Console.WriteLine(JsonConvert.SerializeObject(values));
1) Parse your json to JObject under namespace with using Newtonsoft.Json.Linq;
2) Retrieve 1st object inside parameters array by using JObject.SelectToken()
3) Remove A, B, C by using JObject.Remove()
string json = "Your json here";
JObject jObject = JObject.Parse(json);
JObject jObj = (JObject)jObject.SelectToken("parameters[0]");
jObj.Property("A").Remove();
jObj.Property("B").Remove();
jObj.Property("C").Remove();
string output = jObject.ToString();
Output: (From Debugger)
Online Demo
I am having trouble deserializing JSON received from HubSpot ContactList API.
I am using Restsharp and NewtonSoft, and I'm having real struggles understanding how to correctly define the required classes in order to deserialize the JSON string, which is below:
"contacts": [
{
"vid": 2251,
"portal-id": 5532227,
"is-contact": true,
"profile-url": "https://app.hubspot.com/contacts/5532227/contact/2251",
"properties": {
"firstname": {
"value": "Carl"
},
"lastmodifieddate": {
"value": "1554898386040"
},
"company": {
"value": "Cygnus Project"
},
"lastname": {
"value": "Swann"
}
},
"form-submissions": [],
"identity-profiles": [
{
"vid": 2251,
"saved-at-timestamp": 1553635648634,
"deleted-changed-timestamp": 0,
"identities": [
{
"type": "EMAIL",
"value": "cswann#cygnus.co.uk",
"timestamp": 1553635648591,
"is-primary": true
},
{
"type": "LEAD_GUID",
"value": "e2345",
"timestamp": 1553635648630
}
]
}
],
"merge-audits": []
},
{
"vid": 2301,
"portal-id": 5532227,
"is-contact": true,
"profile-url": "https://app.hubspot.com/contacts/5532227/contact/2301",
"properties": {
"firstname": {
"value": "Carlos"
},
"lastmodifieddate": {
"value": "1554886333954"
},
"company": {
"value": "Khaos Control"
},
"lastname": {
"value": "Swannington"
}
},
"identity-profiles": [
{
"vid": 2301,
"saved-at-timestamp": 1553635648733,
"deleted-changed-timestamp": 0,
"identities": [
{
"type": "EMAIL",
"value": "cswann#khaoscontrol.com",
"timestamp": 1553635648578,
"is-primary": true
},
{
"type": "LEAD_GUID",
"value": "c7f403ba",
"timestamp": 1553635648729
}
]
}
],
"merge-audits": []
}
],
"has-more": false,
"vid-offset": 2401
}
If I simply request the vid, I correctly get 2 vid's back. It's when I try to do the properties and that i get a fail.
Please help
Lets reduce the Json to the minimum to reproduce your error :
{
"vid": 2301,
"portal-id": 5532227,
"is-contact": true,
"profile-url": "https://app.hubspot.com/contacts/5532227/contact/2301",
"properties": {
"firstname": {
"value": "Carlos"
},
"lastmodifieddate": {
"value": "1554886333954"
},
"company": {
"value": "Khaos Control"
},
"lastname": {
"value": "Swannington"
}
}
}
And the appropriate class ContactListAPI_Result:
public partial class ContactListAPI_Result
{
[JsonProperty("vid")]
public long Vid { get; set; }
[JsonProperty("portal-id")]
public long PortalId { get; set; }
[JsonProperty("is-contact")]
public bool IsContact { get; set; }
[JsonProperty("profile-url")]
public Uri ProfileUrl { get; set; }
[JsonProperty("properties")]
public Dictionary<string, Dictionary<string, string>> Properties { get; set; }
}
public partial class ContactListAPI_Result
{
public static ContactListAPI_Result FromJson(string json)
=> JsonConvert.DeserializeObject<ContactListAPI_Result>(json);
//public static ContactListAPI_Result FromJson(string json)
// => JsonConvert.DeserializeObject<ContactListAPI_Result>(json, Converter.Settings);
}
public static void toto()
{
string input = #" {
""vid"": 2301,
""portal-id"": 5532227,
""is-contact"": true,
""profile-url"": ""https://app.hubspot.com/contacts/5532227/contact/2301"",
""properties"": {
""firstname"": {
""value"": ""Carlos""
},
""lastmodifieddate"": {
""value"": ""1554886333954""
},
""company"": {
""value"": ""Khaos Control""
},
""lastname"": {
""value"": ""Swannington""
}
}
}";
var foo = ContactListAPI_Result.FromJson(input);
}
But the Value of one property will be burrow in the sub dictionary, we can the project the object in a more usefull one :
public partial class ItemDTO
{
public long Vid { get; set; }
public long PortalId { get; set; }
public bool IsContact { get; set; }
public Uri ProfileUrl { get; set; }
public Dictionary<string, string> Properties { get; set; }
}
Adding the projection to the Class:
public ItemDTO ToDTO()
{
return new ItemDTO
{
Vid = Vid,
PortalId = PortalId,
IsContact = IsContact,
ProfileUrl = ProfileUrl,
Properties =
Properties.ToDictionary(
p => p.Key,
p => p.Value["value"]
)
};
}
Usage :
var result = foo.ToDTO();
Live Demo
Creating and managing class structure for big and nested key/value pair json is tedious task
So one approach is to use JToken instead.
You can simply parse your JSON to JToken and by querying parsed object, you will easily read the data that you want without creating class structure for your json
From your post it seems you need to retrieve vid and properties from your json so try below code,
string json = "Your json here";
JToken jToken = JToken.Parse(json);
var result = jToken["contacts"].ToObject<JArray>()
.Select(x => new
{
vid = Convert.ToInt32(x["vid"]),
properties = x["properties"].ToObject<Dictionary<string, JToken>>()
.Select(y => new
{
Key = y.Key,
Value = y.Value["value"].ToString()
}).ToList()
}).ToList();
//-----------Print the result to console------------
foreach (var item in result)
{
Console.WriteLine(item.vid);
foreach (var prop in item.properties)
{
Console.WriteLine(prop.Key + " - " + prop.Value);
}
Console.WriteLine();
}
Output:
I have an MVC application, that serializes my model into json schema (using Newtonsoft json.net schema). The problem is that items in my array have type ["string", "null"], but what I need is just "string". Here is code for my class:
public class Form
{
[Required()]
public string[] someStrings { get; set; }
}
This is schema made by Json.net schema:
"someStrings": {
"type": "array",
"items": {
"type": [
"string",
"null"
]
}
}
While I am expecting this:
"someStrings": {
"type": "array",
"items": {
"type": "string"
}
}
Help me get rid of that "null" please.
Try setting DefaultRequired to DisallowNull when you generate the schema:
JSchemaGenerator generator = new JSchemaGenerator()
{
DefaultRequired = Required.DisallowNull
};
JSchema schema = generator.Generate(typeof(Form));
schema.ToString();
Output:
{
"type": "object",
"properties": {
"someStrings": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
You can try this:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
Try this ::
public class Employee
{
public string Name { get; set; }
public int Age { get; set; }
public decimal? Salary { get; set; }
}
Employee employee= new Employee
{
Name = "Heisenberg",
Age = 44
};
string jsonWithNullValues = JsonConvert.SerializeObject(person, Formatting.Indented);
output: with null
// {
// "Name": "Heisenberg",
// "Age": 44,
// "Salary": null
// }
string jsonWithOutNullValues = JsonConvert.SerializeObject(employee, Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
output: without null
// {
// "Name": "Heisenberg",
// "Age": 44
// }
This is the class Database:
namespace vVvBot.Model
{
public class Database
{
public List<CustomCommand> CustomCommands { get; set; }
public List<CustomEvent> CustomEvents { get; set; }
}
}
This is the class for CustomEvent:
namespace vVvBot.Model
{
public class CustomEvent
{
public string Name { get; set; }
public bool Enabled { get; set; }
public List<CustomCommand> Commands { get; set; }
}
}
This is the class CustomCommand:
namespace vVvBot.Model
{
public class CustomCommand
{
public string Keyword { get; set; }
public CommandType Type { get; set; }
public string Message { get; set; }
public bool Enabled { get; set; }
}
}
This is the class where I deserialize and serialize the Database object
namespace vVvBot.Data
{
public class FileJsonContext
{
public Database ReadToObject(string fileName)
{
dynamic jsonData = File.ReadAllText(fileName);
return JsonConvert.DeserializeObject<Database>(jsonData) ?? new Database();
}
public void ObjectToFile(string fileName, Database database)
{
dynamic jsonData = JsonConvert.SerializeObject(database, Formatting.Indented);
File.WriteAllText(fileName, jsonData);
}
}
}
In the file with the issue, this is where I instantiate Database:
private Database _database;
public Database Database => _database ?? (_database = JsonContext.ReadToObject("Commands.txt"));
The line with the issue is:
var messageindex = Database.CustomEvents[index].Commands.FindLastIndex(x => x.Type == CommandType.Welcome);
It tries to complete the line but immediately returns because its comes back with null. There is a List in the custom events that should call the customcommand list so I can access the object. Not sure why it comes back to NULL.
The Json File includes:
{
"CustomCommands": [
{
"Keyword": "Hello",
"Type": 0,
"Message": "World",
"Enabled": true
},
{
"Keyword": "Test",
"Type": 0,
"Message": "test",
"Enabled": true
},
{
"Keyword": "greeting",
"Type": 3,
"Message": "this isnt a test ",
"Enabled": true
},
{
"Keyword": "leaving",
"Type": 4,
"Message": "Sorry to see you go ",
"Enabled": true
},
{
"Keyword": "faq",
"Type": 1,
"Message": "This is a FAQ TEST ",
"Enabled": true
},
{
"Keyword": "Hi",
"Type": 0,
"Message": "Hilo ",
"Enabled": false
},
{
"Keyword": "Hi",
"Type": 0,
"Message": "Hilo ",
"Enabled": false
}
],
"CustomEvents": [
{
"Name": "greeting",
"Enabled": true
},
{
"Name": "leaving",
"Enabled": true
}
]
}
i had this problem a long time ago and i thought the problem was from Unicode.
so i wrote a function for this . you can use it :
List<T> ToJson<T>(byte[] byteArray) where T : new()
{
MemoryStream stream = new MemoryStream(byteArray);
JsonSerializer se = new JsonSerializer();
StreamReader re = new StreamReader(stream);
JsonTextReader reader = new JsonTextReader(re);
return se.Deserialize<List<T>>(reader);
}
and i use this function Like this:
ToJson(data.ToArray());
data is a MemoryStream.