0

I have a JSON string that I receive from a service call that has the following format:

{
  "DataKEY": [
    {
      "value": {
        "timestamp": "2022-10-26T05:00:00Z",
        "value": 0.0
      }
    },
    {
      "value": {
        "timestamp": "2022-10-26T06:00:00Z",
        "value": 0.0
      }
    }
  ]
}

How can I change just the node names of the middle "value" to obtain the following output to send to another service:

{
  "DataKEY": [
    {
      "KEY": {
        "timestamp": "2022-10-26T05:00:00Z",
        "value": 0.0
      }
    },
    {
      "KEY": {
        "timestamp": "2022-10-26T06:00:00Z",
        "value": 0.0
      }
    }
  ]
}

I need to change the first level of nodes with the name "value" to a given "KEY" in the response string, I tried using string.Replace("value", "KEY") but that replaces the inner level as well.

How can I change only the first level of nodes?

Chris Schaller
  • 13,704
  • 3
  • 43
  • 81
Bash
  • 39
  • 6
  • 4
    Since it is obviously JSON, you should rather transform it from one schema to the other instead of mucking around with the string representation. Admittedly, the schema isn't optimal with a `value` object having a `value` property. – Filburt Oct 27 '22 at 18:07
  • I'm confused, because the "expected output" does not match your explanation. Your explanation states that only the first match should be replaced, but the expected output has all matches replaced. – knittl Oct 27 '22 at 18:09
  • See if [Transforming JSON from one structure to another](https://stackoverflow.com/q/55531295/205233) helps. – Filburt Oct 27 '22 at 18:11
  • Only the first match in each response. Notice how the "value: 0.0" is unchanged, yet the KEY has been changed. @knittl – Bash Oct 27 '22 at 18:12
  • Yes, it was a JSON jObject that had been converted to String. Would this change be better to do before converting to string? Sorry, I am new to this. @Filburt – Bash Oct 27 '22 at 18:14
  • So you're replacing multiple instances, not only the first one. Filburt has the only sensible solution: parse from JSON, transform to new schema, serialize to JSON. – knittl Oct 27 '22 at 18:14
  • @Bash Depending on the class/object maybe there's no need for transformation at all. Please add more details about the source of this JSON. – Filburt Oct 27 '22 at 18:18
  • This JSON was returned after making an API call to the KEY value I am trying to add back. I am using Newtonsoft.Json to then parse the response to a jObject and then later a jToken object and finally a string @Filburt – Bash Oct 27 '22 at 18:28
  • Try something like this: https://dotnetfiddle.net/tm8iYs Note that I had to complete your input to make it a valid JSON string ;) – Chris Schaller Oct 27 '22 at 18:32
  • Will give this a try, everything looks good beside the null value, is there a way to get rid of this? I ask because the POST request requires a certain format to work. Thank you so much! @ChrisSchaller – Bash Oct 27 '22 at 18:51
  • Oops, how lazy of me: https://dotnetfiddle.net/YUsRn8 In the serializer settings, set `NullValueHandling` to `Ignore` . Sorry, that was the whole point of why I did it that way ;) – Chris Schaller Oct 27 '22 at 22:22
  • 2
    Voting to re-open because "first string" is a misleading duplicate, this isn't first string at all, in-fact the solution is to not use string manipulation at all, I am genuinely interested myself to see other solutions to this issue – Chris Schaller Oct 27 '22 at 22:25
  • Thank you for your help! I will give this new solution a try now! @ChrisSchaller – Bash Oct 28 '22 at 15:29

1 Answers1

0

When the string represents a JSON data response then you will find it easier to manipulate the names of the properties or the structure of the object graph by first deserializing the data transforming the data to the desired output, then serialising the transformed object back into a string.

If we only need to change the names of some elements, you could use a JSON reader and writer process to step through the file or we can take advantage of some serialization tricks to avoid the transformation step.

We can configure the serialization provider to omit null valued properties which means we can provide 2 properties on a class definition that will be mapped to the same internal backing field, in the example below I have mapped a second property to the inner Key value, this only has a setter making it write-only. This way any deserialization process can write to it via Key or Value named property, but in the serialization process the Value property will always return null and can be omitted from the output:

this is the important aspect and probably the only scenario I've ever used a write-only property:

public DataKeyValue Key { get;set; }
public DataKeyValue Value 
{ 
    get
    {
        return null;
    }
    set 
    { 
        Key = value; 
    }
}

This is the full class definition:

public class DataKeyWrapper
{
    public DataKey[] DataKEY { get;set; }
}
public class DataKey
{
    public DataKeyValue Key { get;set; }
    public DataKeyValue Value 
    { 
        get
        {
            return null;
        }
        set 
        { 
            Key = value; 
        }
    }
}
public class DataKeyValue
{
    public DateTime timestamp { get;set; }
    public Decimal value { get;set; }
}

The you can deserialize and re-serialize with this logic:

using Newtonsoft.Json;
...
var input = JsonConvert.DeserializeObject<DataKeyWrapper>(input);
var output = JsonConvert.SerializeObject(input, new JsonSerializerSettings 
             {
                 Formatting = Newtonsoft.Json.Formatting.Indented, 
                 NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore 
             });

You can view a full fiddle on this here: https://dotnetfiddle.net/YUsRn8

The Response:

{
  "DataKEY": [
    {
      "Key": {
        "timestamp": "2022-10-26T05:00:00Z",
        "value": 0.0
      }
    },
    {
      "Key": {
        "timestamp": "2022-10-26T06:00:00Z",
        "value": 0.0
      }
    }
  ]
}
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81