21
       {
          "TestData":{
              "Year__of__Account":"2019",
              "Tax___x0025_":"0.06",
              "Buildings__1":"1000",
              "Contents__1":"400",
              "Total_Insurable_Value":"100",
              "Buildings__Prem":"2560.8",
              "Contents__Prem":"1707.2",
              "YB__1":"1950",
              "No__Buildings":"55",
              "Location_Sprinklers_YN":"No",
              "test":"test"
           }
        }

In the above sample JSON I want to add a property called "Name" with Value "John" inside property "TestData". How can I achieve this using .net Core 3.0 System.Text.Json library.

I have tried using methods of Utf8JsonWriter but it is creating a new JSON object instead of appending it to the above existing JSON.

        using (MemoryStream memoryStream1 = new MemoryStream())
        {
            using (Utf8JsonWriter utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
            {
                using (JsonDocument jsonDocument = JsonDocument.Parse(json))
                {
                    utf8JsonWriter1.WriteStartObject();
                    utf8JsonWriter1.WritePropertyName("Name");
                    utf8JsonWriter1.WriteStringValue("John");
                    utf8JsonWriter1.WriteEndObject();

                    // how can I add above properties to JsonDocument object??
                }
            }
        }
dbc
  • 104,963
  • 20
  • 228
  • 340
Kunal Patil
  • 261
  • 1
  • 2
  • 5
  • I haven't personally gotten around to installing VS2019/Core3 yet, so I haven't done this myself, but presumably you would load the document in with `JsonDocument.Parse()`, as you do, add the new attribute, and then write it out with [JsonDocument.WriteTo()](https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsondocument.writeto?view=netcore-3.0#System_Text_Json_JsonDocument_WriteTo_System_Text_Json_Utf8JsonWriter_). – 500 - Internal Server Error Oct 09 '19 at 11:00
  • yes, exactly my question how can I add new attribute after loading JsonDocument? I am unable to find any method in their documentation. – Kunal Patil Oct 09 '19 at 11:27
  • `JsonDocument` is read-only. There is an open issue [Writable Json DOM #39922](https://github.com/dotnet/corefx/issues/39922) tracking this. Related but not duplicate: [Modifying a JSON file using System.Text.Json](https://stackoverflow.com/q/58997718/3744182). – dbc Nov 25 '19 at 23:56
  • Were you able to resolve this issue? I am having issues with inserting property at specified location, so wondering if you could help with the following [question](https://stackoverflow.com/questions/62140014/adding-a-property-into-specified-location-into-json-using-newtonsoft-json). I can use both Newtonsoft or System.Text.Json. – learner Jun 02 '20 at 09:30
  • 1
    You cannot add property using System.Text.Json. Use Newtonsoft JObject to add properties to your Json Object. Refer this -> [JObject](https://www.newtonsoft.com/json/help/html/Methods_T_Newtonsoft_Json_Linq_JObject.htm) – Kunal Patil Jun 03 '20 at 10:45

5 Answers5

17

Starting from .NET 6 you can use JsonNode. This is a modifiable, dictionary-backed API to complement the readonly JsonDocument.

For your example, the solution would be as follows:

var jsonNode = JsonNode.Parse(json);
jsonNode["TestData"]["Name"] = "John";
Yves
  • 632
  • 1
  • 11
  • 17
12

Assuming there may be several properties and you want to add a name only to "TestData" property:

using (MemoryStream memoryStream1 = new MemoryStream())
{
    using (Utf8JsonWriter utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
    {
        using (JsonDocument jsonDocument = JsonDocument.Parse(json))
        {
            utf8JsonWriter1.WriteStartObject();

            foreach (var element in jsonDocument.RootElement.EnumerateObject())
            {
                if (element.Name == "TestData")
                {
                    utf8JsonWriter1.WritePropertyName(element.Name);

                    // Staring new object
                    utf8JsonWriter1.WriteStartObject();

                    // Adding "Name" property 
                    utf8JsonWriter1.WritePropertyName("Name");
                    utf8JsonWriter1.WriteStringValue("John");

                    // Copying existing values from "TestData" object
                    foreach (var testDataElement in element.Value.EnumerateObject())
                    {
                        testDataElement.WriteTo(utf8JsonWriter1);
                    }

                    utf8JsonWriter1.WriteEndObject();
                }
                else
                {
                    element.WriteTo(utf8JsonWriter1);
                }
            }

            utf8JsonWriter1.WriteEndObject();
        }
    }

    var resultJson = Encoding.UTF8.GetString(memoryStream1.ToArray());
}

Here for each property (except for "TestData" property) I write the whole value as is (by calling element.WriteTo(utf8JsonWriter1)), and for "TestData" property I start a new object, add "Name" property and then copy each of the "TestData" object's properties.

P.S. This works, but I'm pretty sure a much better solution should exist.

Igor
  • 600
  • 6
  • 13
  • 5
    "I'm pretty sure a much better solution should exist." exactly – user1496062 Oct 23 '19 at 03:17
  • It is kind of a workaround since you are creating a new JSON object from an existing one. But maybe this is the only solution to my problem since there are no methods available in System.Text.Json library for JSON object manipulation. – Kunal Patil Oct 24 '19 at 13:43
4

Here is a possible answer

static void Main(string[] args)
    {
        var jsonString = @"
        {
            ""TestData"":{
                ""Year__of__Account"":""2019"",
                ""Tax___x0025_"":""0.06"",
                ""Buildings__1"":""1000"",
                ""Contents__1"":""400"",
                ""Total_Insurable_Value"":""100"",
                ""Buildings__Prem"":""2560.8"",
                ""Contents__Prem"":""1707.2"",
                ""YB__1"":""1950"",
                ""No__Buildings"":""55"",
                ""Location_Sprinklers_YN"":""No"",
                ""test"":""test""
            }
        }
        ";

        var jsonDoc = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);

        var testDataDict = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonDoc["TestData"].ToString());

        testDataDict.Add("Name", "John");

        //replace the test data with the modified test data
        jsonDoc["TestData"] = testDataDict;

        Console.WriteLine(JsonSerializer.Serialize(jsonDoc));
    }
Khurshid Ali
  • 141
  • 1
  • 2
4

I've just created a NuGet package with some hopefully useful extension methods for JsonElement, which allow properties to be added and/or removed. It's based on using the Utf8JsonWriter to create a new mutated JsonElement based on the original, like the answer above.

GitHub repo | NuGet package

var jsonString = "{ \"Name\": \"Andrew\", \"EmailAddress\": \"a@b.com\" }";
var jElement = JsonDocument.Parse(jsonString).RootElement;

jElement = jElement.AddProperty("Age", 38)
.AddProperty("Male", true)
.AddProperty("Female", false)
.AddNullProperty("Alien")
.AddProperty("Roles", new string[] { "admin", "user" })
.AddProperty("LastUpdated", DateTime.UtcNow)
.AddProperty("crazyNewObject", new
{
    Name = "Hobbies",
    Value = "bass guitar and writing c# code"
});

Hopefully someone will find them useful, but if they don't do quite what you need, please enhance and submit a pull request.

juwens
  • 3,729
  • 4
  • 31
  • 39
ajp
  • 195
  • 8
0

Using JsonSerializer to deserialize into a nested dictionary is also possible:

static void Main(string[] args)
{
    string testJson = @"
    {
    ""TestData"":{
        ""Year__of__Account"":""2019"",
        ""Tax___x0025_"":""0.06"",
        ""Buildings__1"":""1000"",
        ""Contents__1"":""400"",
        ""Total_Insurable_Value"":""100"",
        ""Buildings__Prem"":""2560.8"",
        ""Contents__Prem"":""1707.2"",
        ""YB__1"":""1950"",
        ""No__Buildings"":""55"",
        ""Location_Sprinklers_YN"":""No"",
        ""test"":""test""
        }
    }";

    using (var memoryStream1 = new MemoryStream())
    {
        using (var utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
        {
            //For each level in json tree an additional dictionary must be added
            var jsonDict = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, object>>>(testJson);
            jsonDict["TestData"].Add("Name", "John");
            JsonSerializer.Serialize<object>(utf8JsonWriter1, jsonDict);
        }

        string testString = Encoding.UTF8.GetString(memoryStream1.ToArray());
    }
}

But note that the new property is always added at the end of the TestData block.

Quergo
  • 888
  • 1
  • 8
  • 21