Equivalent of JArray.FromObject() in .Net5?

924 views Asked by At

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"
}
2

There are 2 answers

1
dbc On BEST ANSWER

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.

0
Reza Heidari On

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);