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);
Related
{
"objects": [
{
"id": 123,
"tracking_datas": [
{
"id": 1,
"polygons": [1,3]
},
{
"id": 2,
"polygons": [3]
},
{
"id": 3,
"polygons": [1,2]
}
]
}
]
}
I have a json file as above. And there is a model that satisfies this json in my NetCore project. I want to get objects containing polygonIds that I have determined with the help of mongodb. How can I do this with c# mongo db?
For example, I have a reference array requiredPolygons: [1,2] and I want to get the data containing these polygon'ids in the tracking data of the objects in the json. The expected result is as follows.
{
"objects":
[
{
"id": 123,
"tracking_datas":[
{
"id": 1,
"polygons": [1,3]
},
{
"id": 3,
"polygons": [1,2]
}
]
}
]
}
public class Test
{
public ObjectId Id { get; set; }
public IEnumerable<Object> objects { get; set; }
[BsonExtraElements]
public BsonDocument UnmappedFields { get; set; } // I'm not sure why it's required, something wrong with mapping configuration,
// but it's a separate question
}
public class Object
{
public int id { get; set; }
public IEnumerable<TrackingData> tracking_datas { get; set; }
}
public class TrackingData
{
public int id { get; set; }
public IEnumerable<int> polygons { get; set; }
[BsonExtraElements]
public BsonDocument UnmappedFields { get; set; } // I'm not sure why it's required, something wrong with mapping configuration,
// but it's a separate question
}
var json = #"{
""objects"": [
{
""id"": 123,
""tracking_datas"": [
{
""id"": 1,
""polygons"": [1,3]
},
{
""id"": 2,
""polygons"": [3]
},
{
""id"": 3,
""polygons"": [1,2]
}
]
}
]
}";
var client = new MongoClient();
var db = client.GetDatabase("so_test");
var coll = db.GetCollection<BsonDocument>("coll");
coll.InsertOne(BsonDocument.Parse(json));
var ids = new[] { 1, 2 };
var typedColl = db.GetCollection<Test>("coll");
var result = typedColl
.Aggregate()
.Project(p =>
new Test
{
Id = p.Id,
objects = p.objects.Select(o =>
new Object
{
id = o.id,
tracking_datas = o.tracking_datas.Where(t => t.polygons.Any(p=>ids.Contains(p)))
})
}
)
.ToList();
Here you go:
db.collection.find({
"objects.tracking_datas.polygons": {
$in: [
1,
2
]
}
})
https://mongoplayground.net/p/MDlIV3YPkZB
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 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!
I have following json:
{
"Australia": {
"count": 2,
"records": {
"File1.ppt": {
"id": "123456789"
},
"File2.doc": {
"id": "987654321"
}
}
},
"PDFs.zip": {
"count": 0,
"records": {}
},
"Peru": {
"count": 2,
"records": {
"File3.PPT": {
"id": "897456123"
},
"File4.PPT": {
"id": "123546789"
}
}
},
"total count": 4
}
and to deserialize the above json I have defined some classes so that I can use these classes while deserializing my json into objects and below are the classes:
namespace GEO_Batch_Creation
{
[DataContract]
class BhpIdJson
{
[DataMember(Name = "objects")]
public Dictionary<string, Country[]> Countries { get; set; }
[DataMember(Name = "total count")]
public int TotalCount { get; set; }
}
[DataContract]
class Country
{
[DataMember(Name = "count")]
public int Count { get; set; }
[DataMember(Name = "records")]
public Dictionary<string, Record> Records { get; set; }
}
[DataContract]
class Record
{
[DataMember(Name = "filename")]
public string FileName { get; set; }
[DataMember(Name = "id")]
public Dictionary<string, string> BhpId { get; set; }
}
}
But when I use following code to deserialize the json I am getting only total count.
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
// Deserialization from JSON
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(BhpIdJson));
BhpIdJson bsObj2 = (BhpIdJson)deserializer.ReadObject(ms);
}
Please suggest me where I am doing mistake.
I don't think that this JSON is in correct format. I don't know if you got this from somewhere or made for yourself, but if the last one I recommend you to change the structure.
Even in C# you cant realy create a class that has the objects and the count of the object in the same List or Array etc.
Based on your class your JSON yhould look like this:
{
"objects": [
{
"name": "Australia",
"count": 2,
"records": [
{
"fileName": "File1.ppt",
"id": "123456789"
},
{
"fileName": "File2.doc",
"id": "987654321"
}
]
}
],
"total count": 4
}
As you can see you have to add Name or something to your Country class. I hope it helped.
Edit:
You can create a list like I mentioned above but it's not a good practice I think.
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) } ));