Related
We have a project which using System.Text.Json in .NET 5 instead of Newtonsoft JObject. Using Newtonsoft, it is pretty easy to replace dynamic JSON data e.g. as shown below:
siteDataObject["student"] = JArray.FromObject(studentservice.GetStudents());
When studentservice.GetStudents() is return List as below structure
internal class Student {
public int Id { get; set; }
public string Name { get; set; }
public string ContactPhone { get; set; }
public IEnumerable<MedicalRecord> MedicalRecords { get; set; }
}
internal class MedicalRecord {
public int Id { get; set; }
public string Name { get; set; }
public DateTime RecordDate { get; set; }
public IEnumerable<DiseaseLog> DiseaseLogs{ get; set; }
}
internal class DiseaseLog {
public int Id { get; set; }
public string Name { get; set; }
public DateTime LogDate { get; set; }
}
but in System.Text.Json
foreach (var element in doc.RootElement.EnumerateObject()) {
if (element.Name == "student") {
writer.WritePropertyName(element.Name);
}
else {
element.WriteTo(writer);
}
}
I don't know how to convert List<student> into JSON array data, when student class have many properties with multi collection inside.
Can anyone advise how to convert it ?
To clarify, I need to propose the full code for this, I have a dynamic json string and want to replace element : students into new record, the code will be
var dynamicJson = #"{'roomid':1,'roomcode':'Code001','students':[1],'contentdata':'say hello','footerdata':'cookie policy'}";
using MemoryStream stream = new MemoryStream();
using Utf8JsonWriter writer = new Utf8JsonWriter(stream);
using var dynamicDocument = JsonDocument.Parse(dynamicJson);
writer.WriteStartObject();
foreach (var element in dynamicDocument.RootElement.EnumerateObject())
{
if (element.Name == "students")
{
// unknown how to modify the student record into array
}
else
element.WriteTo(writer);
}
writer.WriteEndObject();
stream.Flush();
var modifyJson = Encoding.UTF8.GetString(stream.ToArray());
I know how to modify student value , if student element is string, but I don't know how to modify it into array, by using simple code. As student have multi class inside.
My expected result should be
{
"roomid": 1,
"roomcode": "Code001",
"students": [
{
"id": 1,
"Name": "Wilson",
"ContactPhone": "123-122-3311",
"MedicalRecords": [
{
"id": 101,
"Name ": "Medial record 101011",
"RecordDate": "2021-12-31",
"DiseaseLogs": [
{
"id": 18211,
"Name ": "Patient Log 19292",
"LogDate": "2020-1-31"
},
{
"id": 18212,
"Name ": "Patient Log 2911w",
"LogDate": "2020-3-31"
}
]
}
]
}
],
"contentdata": "say hello",
"footerdata": "cookie policy"
}
In .NET 5 there is no modifiable JSON Document Object Model built into to System.Text.Json. JsonDocument is read-only, and System.Text.Json.Nodes was only introduced in .NET 6. Thus, the easiest way to deserialize, modify and re-serialize free-form JSON in .NET 5 is to deserialize to some partial data model, with unknown values bound into a dictionary.
If you do not care about the order of properties at the root level, you could deserialize to a model with a public object students { get; set; } property, and bind the remaining elements to a JsonExtensionData overflow dictionary:
public class RootObject
{
public object students { get; set; }
[System.Text.Json.Serialization.JsonExtensionDataAttribute]
public IDictionary<string, object> ExtensionData { get; set; }
}
Then deserialize, modify and re-serialize as follows:
var students = new List<Student> { /* Initialize these as required... */ };
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var root = JsonSerializer.Deserialize<RootObject>(dynamicJson);
root.students = students;
var modifyJson = JsonSerializer.Serialize(root, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true });
Which results in
{
"students": [
{
"id": 1,
"name": "Wilson",
"contactPhone": "123-122-3311",
"medicalRecords": [
{
"id": 101,
"name": "Medial record 101011",
"recordDate": "2021-12-31T00:00:00",
"diseaseLogs": [
{
"id": 18211,
"name": "Patient Log 19292",
"logDate": "2020-01-31T00:00:00"
},
{
"id": 18212,
"name": "Patient Log 2911w",
"logDate": "2020-03-31T00:00:00"
}
]
}
]
}
],
"roomid": 1,
"roomcode": "Code001",
"contentdata": "say hello",
"footerdata": "cookie policy"
}
the students property must be declared as object because the input JSON already has an array containing a single integer value; declaring it as public List<Student> students { get; set; } would result in a deserialization when initially loading the JSON.
Demo fiddle #1 here.
If you do care about the order of properties at the root level, you could deserialize to an OrderedDictionary (an old order-preserving non-generic dictionary dating from .NET Framework 2.0 which is still around and supported), overwrite the "students" value, and re-serialize:
var students = new List<Student> { /* Initialize these as required... */ };
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var root = JsonSerializer.Deserialize<OrderedDictionary>(dynamicJson);
root["students"] = students;
var modifyJson = JsonSerializer.Serialize(root, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true });
Which results in
{
"roomid": 1,
"roomcode": "Code001",
"students": [
{
"id": 1,
"name": "Wilson",
"contactPhone": "123-122-3311",
"medicalRecords": [
{
"id": 101,
"name": "Medial record 101011",
"recordDate": "2021-12-31T00:00:00",
"diseaseLogs": [
{
"id": 18211,
"name": "Patient Log 19292",
"logDate": "2020-01-31T00:00:00"
},
{
"id": 18212,
"name": "Patient Log 2911w",
"logDate": "2020-03-31T00:00:00"
}
]
}
]
}
],
"contentdata": "say hello",
"footerdata": "cookie policy"
}
Demo fiddle #2 here.
In .NET 6 this all becomes easier through use of the System.Text.Json.Nodes editable JSON Document Object Model:
var dynamicJson = #"{""roomid"":1,""roomcode"":""Code001"",""students"":[1],""contentdata"":""say hello"",""footerdata"":""cookie policy""}";
var nodes = JsonSerializer.Deserialize<JsonObject>(dynamicJson);
nodes["students"] = JsonSerializer.SerializeToNode(students, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
var modifyJson = nodes.ToString();
But in .NET 5 this is not possible. Demo fiddle #3 here.
I think this is what you want (array or single nested object):
var student = new Student()
{
Name = "Student",
ContactPhone = "contact",
Id = 1,
MedicalRecords = new List<MedicalRecord>()
{
new MedicalRecord()
{
Name = "Medical Record 1",
RecordDate= DateTime.Now ,
Id = 1 ,
MedicalRecords = new List<DiseaseLog>()
{
new DiseaseLog(){ Name = "some disease" ,
LogDate = DateTime.Now, Id =1 }
}
}
}
};
var data = System.Text.Json.JsonSerializer.Serialize(student);
Console.WriteLine(data);
var list = new List<Student>();
list.Add(student);
var arrayData = System.Text.Json.JsonSerializer.Serialize(list);
Console.WriteLine(arrayData);
I have a json config file with values as variables. I want to replace these variables with actual values that are stored in another json file.
Source data json file:
{
"person": {
"id": 15305,
"gender": "Male",
"dob": "1958-10-31T00:00:00",
"name": {
"id": 80587,
"first": "John",
"last": "Dominick"
}
},
"ethnicity": [
{
"id": 2,
"display": "Not Hispanic or Latino",
"kbEthnicity": null
}
],
"race": [
{
"id": 4,
"display": "Black"
},
{
"id": 6,
"display": "Other Race"
}
]
}
Destination data json file:
{
"dryRun": "true",
"person": {
"firstName": "[person.name.first]",
"lastName": "[person.name.last]",
"dateOfBirth": "[person.dob]",
"gender": "[person.gender]",
"race": "[race]"
}
}
Final json file that I want:
{
"dryRun": "true",
"person": {
"firstName": "John",
"lastName": "Dominick",
"dateOfBirth": "1958-10-31T00:00:00",
"gender": "MALE",
"race": "Black, Other Race"
}
}
Here is the code that I wrote using the following SO post that reads both files and is able to extract the correct values from the source file, but I am stuck on what is the best way to create a final json file.
public static void Main(string[] args)
{
var sourceJson = File.ReadAllText("./sourcedata.json");
var srcObj = JObject.Parse(sourceJson);
var destJson = File.ReadAllText("./destinationdata.json");
var destObj = JObject.Parse(destJson);
var result = destObj.Descendants()
.OfType<JProperty>()
.Select(p => new KeyValuePair<string, object>(p.Path,
p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
? null : p.Value));
foreach (var kvp in result)
{
var isVariable = kvp.Value == null ? false : Regex.IsMatch(kvp.Value.ToString(), #"^\[.*?\]$");
if (isVariable)
{
var variable = Regex.Replace(kvp.Value.ToString(), #"[\[\]]+", "");
Console.WriteLine($"Key: {kvp.Key} Value: {kvp.Value} Updated: {srcObj.SelectToken(variable)}");
}
else
{
Console.WriteLine($"Key: {kvp.Key} Value: {kvp.Value}");
}
}
}
Another issue that I want to solve is how to concatenate the values if there is an array? Any help would be appreciated.
High level: You need two POCO objects: one to deserialize your source json (with the values), and one to serialize to your destination json.
You read in the source, deserialize to the source poco, translate from source poco to destination poco, and then serialize that as destination json.
I think that's much more straightforward than working with JObjects, etc.
Here is a slightly simplified version of your objects:
using System;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
var json = #"{""person"":{""name"": {""first"": ""John"",""last"": ""Dominick""}}}";
var objA = Newtonsoft.Json.JsonConvert.DeserializeObject<Source.MyObject>(json);
Console.WriteLine("FirstName: " + objA.Person.Name.FirstName);
var objB = new Destination.MyObject(objA);
var outJson = Newtonsoft.Json.JsonConvert.SerializeObject(objB);
Console.WriteLine(outJson);
}
}
public class Source{
public class MyObject
{
[JsonProperty("person")]
public Person Person { get; set; }
}
public class Person
{
[JsonProperty("name")]
public Name Name { get; set; }
}
public class Name
{
[JsonProperty("first")]
public string FirstName { get; set; }
[JsonProperty("last")]
public string LastName { get; set; }
}
}
public class Destination{
public class MyObject{
public MyObject(Source.MyObject sourceObject){
Person = new Person();
Person.FirstName = sourceObject.Person.Name.FirstName;
Person.LastName = sourceObject.Person.Name.LastName;
}
[JsonProperty("person")]
public Person Person {get;set;}
}
public class Person
{
[JsonProperty("firstName")]
public string FirstName {get;set;}
[JsonProperty("lastName")]
public string LastName {get;set;}
}
}
Output:
FirstName: John
{"person":{"firstName":"John","lastName":"Dominick"}}
see:
https://dotnetfiddle.net/0V4pEO
I have below class structures,
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Message
{
public int Size { get; set; } = 10;
public Student Student { get; set; }
}
Now I have list of messages like,
var messages = new List<Message>
{
new Message{ Size=10, Student=new Student{Id=1, Name="N1" } },
new Message{ Size=20, Student=new Student{Id=2, Name="N2" } }
};
which I want to write in JSON using Newtonsoft.Json,
using (var f = File.CreateText("C:\\Temp\\test.json"))
{
f.Write(JsonConvert.SerializeObject(messages));
}
This give below JSON output,
[
{
"Size": 10,
"Student": {
"Id": 1,
"Name": "N1"
}
},
{
"Size": 20,
"Student": {
"Id": 2,
"Name": "N2"
}
}
]
But I would like below output (without size and only one time root element student),
{
"Student": [
{
"Id": 1,
"Name": "N1"
},
{
"Id": 2,
"Name": "N2"
}
]
}
whats need to be done here? Thanks!
You need to refine your data to get desired result
first
var refindedResult = new { Students = messages.Select(m => m.Student) };
second pass it to json convertor
using (var f = File.CreateText("C:\\Temp\\test.json"))
{
f.Write(JsonConvert.SerializeObject(refindedResult ));
}
You can try to write a StudentModel class.
public class StudentModel
{
public IEnumerable<Student> Student { get; set; }
}
use linq get student collection from messages, then SerializeObject on it.
var res = new StudentModel()
{
Student = messages.Select(x => x.Student)
};
var json = JsonConvert.SerializeObject(res);
//f.Write(JsonConvert.SerializeObject(messages));
f.Write(JsonConvert.SerializeObject(messages.Select(m => m.Student)));
or, when you want a root object (Students, not Student)
f.Write(JsonConvert.SerializeObject(
new { Students = messages.Select(m => m.Student) } ));
I'm using json.net to serialize an object to a json string. Now I have a list of Objects which I like to serialize into a Json array. However, I'm unable to do that with json.net and hope someone can point out my mistake.
I have the following classes:
class PeopleList {
public Person inputs { get; set; }
}
class Person {
public String name { get; set; }
public int age { get; set; }
}
I'm using the following code to serialize the objects:
var json = new List<PeopleList>();
Person p1 = new Person { name = "Name 1", age = 20 };
json.Add(new PeopleList { inputs = p1 });
Person p2 = new Person { name = "Name 2", age = 30 };
json.Add(new PeopleList { inputs = p2 });
string jsonString = JsonConvert.SerializeObject(json, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented });
This gives me the following output:
[
{
"inputs": {
"name": "Name 1",
"age": 20
}
},
{
"inputs": {
"name": "Name 2",
"age": 30
}
}
]
Here is what I actually want:
[
{
"inputs": [
{
"name": "Name 1",
"age": 20
}
]
},
{
"inputs": [
{
"name": "Name 2",
"age": 30
}
]
}
]
As you see I need every object in my list encapsulated with []. How can I achieve that with Json.net? Thanks!
If you want your inputs to be an array, you need to declare it as an array in your object :
class PeopleList {
public List<Person> inputs { get; set; }
}
Then you can use it :
var json = new List<PeopleList>();
List<Person> p1 = new List<Person> { new Person { name = "Name 1", age = 20 } };
json.Add(new PeopleList { inputs = p1 });
List<Person> p2 = new List<Person> { new Person { name = "Name 2", age = 30 } };
json.Add(new PeopleList { inputs = p2 });
string jsonString = JsonConvert.SerializeObject(json, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.Indented });
based on your output and what you want you probably want to do something like this
Json2CSharpClass Converter
public class Person
{
public string name { get; set; }
public int age { get; set; }
}
public class PeopleList
{
public List<Person> inputs { get; set; }
}
In my web app I'm using Newtonsoft.Json and I have following object
[Newtonsoft.Json.JsonObject(Title = "MyCar")]
public class Car
{
[Newtonsoft.Json.JsonProperty(PropertyName = "name")]
public string Name{get;set;}
[Newtonsoft.Json.JsonProperty(PropertyName = "owner")]
public string Owner{get;set;}
}
and I want serialize them with root name (class name). This is desired format using
{'MyCar':
{
'name': 'Ford',
'owner': 'John Smith'
}
}
I know that I can do that with anonymous object, but is any property or another way in Newtonsoft.Json library?
Use anonymous class
Shape your model the way you want using anonymous classes:
var root = new
{
car = new
{
name = "Ford",
owner = "Henry"
}
};
string json = JsonConvert.SerializeObject(root);
I found an easy way to render this out... simply declare a dynamic object and assign the first item within the dynamic object to be your collection class...This example assumes you're using Newtonsoft.Json
private class YourModelClass
{
public string firstName { get; set; }
public string lastName { get; set; }
}
var collection = new List<YourModelClass>();
var collectionWrapper = new {
myRoot = collection
};
var output = JsonConvert.SerializeObject(collectionWrapper);
What you should end up with is something like this:
{"myRoot":[{"firstName":"John", "lastName": "Citizen"}, {...}]}
You can easily create your own serializer
var car = new Car() { Name = "Ford", Owner = "John Smith" };
string json = Serialize(car);
string Serialize<T>(T o)
{
var attr = o.GetType().GetCustomAttribute(typeof(JsonObjectAttribute)) as JsonObjectAttribute;
var jv = JValue.FromObject(o);
return new JObject(new JProperty(attr.Title, jv)).ToString();
}
Sorry, my english is not that good. But i like to improve the upvoted answers.
I think that using Dictionary is more simple and clean.
class Program
{
static void Main(string[] args)
{
agencia ag1 = new agencia()
{
name = "Iquique",
data = new object[] { new object[] {"Lucas", 20 }, new object[] {"Fernando", 15 } }
};
agencia ag2 = new agencia()
{
name = "Valparaiso",
data = new object[] { new object[] { "Rems", 20 }, new object[] { "Perex", 15 } }
};
agencia agn = new agencia()
{
name = "Santiago",
data = new object[] { new object[] { "Jhon", 20 }, new object[] { "Karma", 15 } }
};
Dictionary<string, agencia> dic = new Dictionary<string, agencia>
{
{ "Iquique", ag1 },
{ "Valparaiso", ag2 },
{ "Santiago", agn }
};
string da = Newtonsoft.Json.JsonConvert.SerializeObject(dic);
Console.WriteLine(da);
Console.ReadLine();
}
}
public class agencia
{
public string name { get; set; }
public object[] data { get; set; }
}
This code generate the following json (This is desired format)
{
"Iquique":{
"name":"Iquique",
"data":[
[
"Lucas",
20
],
[
"Fernando",
15
]
]
},
"Valparaiso":{
"name":"Valparaiso",
"data":[
[
"Rems",
20
],
[
"Perex",
15
]
]
},
"Santiago":{
"name":"Santiago",
"data":[
[
"Jhon",
20
],
[
"Karma",
15
]
]
}
}
string Json = JsonConvert.SerializeObject(new Car { Name = "Ford", Owner = "John Smith" }, Formatting.None);
for the root element use GlobalConfiguration.
A very simple approach for me is just to create 2 classes.
public class ClassB
{
public string id{ get; set; }
public string name{ get; set; }
public int status { get; set; }
public DateTime? updated_at { get; set; }
}
public class ClassAList
{
public IList<ClassB> root_name{ get; set; }
}
And when you going to do serialization:
var classAList = new ClassAList();
//...
//assign some value
//...
var jsonString = JsonConvert.SerializeObject(classAList)
Lastly, you will see your desired result as the following:
{
"root_name": [
{
"id": "1001",
"name": "1000001",
"status": 1010,
"updated_at": "2016-09-28 16:10:48"
},
{
"id": "1002",
"name": "1000002",
"status": 1050,
"updated_at": "2016-09-28 16:55:55"
}
]
}
Hope this helps!
Well, you can at least tell Json.NET to include the type name: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm . Newtonsoft.Json.JsonSerializer jser = new Newtonsoft.Json.JsonSerializer();
jser.TypeNameHandling = TypeNameHandling.Objects;
The type will be included at the beginning in the "$type" property of the object.
This is not exactly what you are looking for, but it was good enough for me when facing a similiar problem.
Writing a custom JsonConverter is another approach mentioned in similar questions. However, due to nature of how JsonConverter is designed, using that approach for this question is tricky, as you need to be careful with the WriteJson implementation to avoid getting into infinite recursion: JSON.Net throws StackOverflowException when using [JsonConvert()].
One possible implementation:
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//JToken t = JToken.FromObject(value); // do not use this! leads to stack overflow
JsonObjectContract contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
writer.WriteStartObject();
writer.WritePropertyName(value.GetType().Name);
writer.WriteStartObject();
foreach (var property in contract.Properties)
{
// this removes any property with null value
var propertyValue = property.ValueProvider.GetValue(value);
if (propertyValue == null) continue;
writer.WritePropertyName(property.PropertyName);
serializer.Serialize(writer, propertyValue);
//writer.WriteValue(JsonConvert.SerializeObject(property.ValueProvider.GetValue(value))); // this adds escaped quotes
}
writer.WriteEndObject();
writer.WriteEndObject();
}
I hope this help.
//Sample of Data Contract:
[DataContract(Name="customer")]
internal class Customer {
[DataMember(Name="email")] internal string Email { get; set; }
[DataMember(Name="name")] internal string Name { get; set; }
}
//This is an extension method useful for your case:
public static string JsonSerialize<T>(this T o)
{
MemoryStream jsonStream = new MemoryStream();
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
serializer.WriteObject(jsonStream, o);
var jsonString = System.Text.Encoding.ASCII.GetString(jsonStream.ToArray());
var props = o.GetType().GetCustomAttributes(false);
var rootName = string.Empty;
foreach (var prop in props)
{
if (!(prop is DataContractAttribute)) continue;
rootName = ((DataContractAttribute)prop).Name;
break;
}
jsonStream.Close();
jsonStream.Dispose();
if (!string.IsNullOrEmpty(rootName)) jsonString = string.Format("{{ \"{0}\": {1} }}", rootName, jsonString);
return jsonString;
}
//Sample of usage
var customer = new customer {
Name="John",
Email="john#domain.com"
};
var serializedObject = customer.JsonSerialize();
[Newtonsoft.Json.JsonObject(Title = "root")]
public class TestMain
this is the only attrib you need to add to get your code working.