2
\$\begingroup\$

I have a json string ProcessValue in which I need to extract a particular key (client_id) value and then I need to encode that value using HttpUtility.HtmlEncode and put it back in the same JSON string. ProcessValue is one of the header name which has a json value in it.

Below is my code which does that but I am deserializing twice looks like. Is there any way to improve below code?

ProcessValue = GetHeader(headers, PROCESS_DATA); // LINE A
if (!string.IsNullOrWhiteSpace(ProcessValue))
{
    dynamic jsonObject = Newtonsoft.Json.JsonConvert.DeserializeObject(ProcessValue);
    if (jsonObject.client_id != null)
    {
        var value = jsonObject.client_id;
        jsonObject.client_id = HttpUtility.HtmlEncode(value);
        var modifiedJsonString = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObject);
        ProcessValue = modifiedJsonString;
    }
    ProcCustomer = DeserializeJson<ProcCustomer>(ProcessValue);
}

private T DeserializeJson<T>(string str) where T : new()
{
    try
    {
        return JsonSerializer.Deserialize<T>(str);
    }
    catch (Exception e)
    {
        return new T();
    }
}

Here ProcessValue at LINE A is:

{"key1":"value1","key2":"value2","key3":"value3","key4":"value4","key5":"value6","client_id":"Hänzendes"}

I am extracting client_id value and then encoding it and after that putting it back to same json. And then I am deserializing this modified json value to ProcCustomer POCO. ProcCustomer is the POCO class which holds all the fields value. So I am deserializing and serializing to get my updated json string ProcessValue and then I deserialize this json to ProcCustomer POCO class.

There are places where we use ProcessValue json string directly as it is one of the header value and using ProcCustomer POCO fields as well so that is why I was doing above things. I wanted to see if there is anything I can do to optimize my above code where I don't have to deserialize and then serialize and then again deserialize to put it in POCO?

Also I am mixing Utf8Json library with Newton json library. I prefer to use Utf8 json library for both serialization and deserialization in my above code.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Even if ProcessValue is used as string in other objects, why you don't just deserialize it once across the class, then use the POCO object directly. Then, serialize it whenever needed.

So, in your question this would be valid :

ProcessValue = GetHeader(headers, PROCESS_DATA); // LINE A

if (!string.IsNullOrWhiteSpace(ProcessValue))
{
    ProcCustomer = DeserializeJson<ProcCustomer>(ProcessValue);

    if (ProcCustomer != null && !string.IsNullOrWhiteSpace(ProcCustomer.ClientId))
    {
        ProcCustomer.ClientId = HttpUtility.HtmlEncode(ProcCustomer.ClientId);
    }
}

This way you only deserialized it once in this scope. Also, if the client_id is always needs to be encoded using HttpUtility.HtmlEncode you could modify the POCO and do this for instance :

public class ProcCustomer
{
    //.. other properties 
    
    [JsonProperty("client_id")]
    public string ClientId 
    { 
        get => ClientId;
        set
        {
            if(!string.IsNullOrEmpty(value))
            {
                ClientId = HttpUtility.HtmlEncode(value);
            }
            else 
            {
                ClientId = value;
            }
        } 
    } 
}

With this, your code will be shorter as you only need to deserialize, the rest will be updated according to the POCO logic!

Answering the comments :

working with ProcessValue as string, will make things difficult to handle. What I suggested is to use the deserialized object (which is the POCO in your case), then work with it like any object. When you want to pass it to the HttpClient header, just serialize it, to pass it as sting.

The main idea is to work with objects, and not with the strings! use the strings only when required. This way you'll have a better coding experience.

For example, we can modify the POCO and override the ToString method like this :

public class ProcCustomer
{
    public override string ToString() {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }
}

Now, when we want to add it to the header we can do this (example) :

// assuming we've already updated the POCO values
// and we need to add it to the HttpClient header 
using (var client = new HttpClient{ BaseAddress = baseAddress })
{
    client.DefaultRequestHeaders.Add("", ProcCustomer.ToString());
    
    using (var response = await client.GetAsync(Url))
    {
        var responseData = await response.Content.ReadAsStringAsync();
    }
}

If you can't modify the POCO, then you can add extensions like this :

// implement the extensions 
public class JsonExtensions
{
    public T JsonDeserialize<T>(this string str) where T : class
    {
        try
        {
            return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(str);
        }
        catch (Exception e)
        {
            return default;
        }
    }
    
    public string JsonSerialize<T>(this T obj) where T : class
    {
        try
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject<T>(obj);
        }
        catch (Exception e)
        {
            return null;
        }
    }
}

You can use the extensions instead of ToString approach

// EXAMPLE : 

ProcessValue = GetHeader(headers, PROCESS_DATA); // LINE A

if (!string.IsNullOrWhiteSpace(ProcessValue))
{
    ProcCustomer = ProcessValue.DeserializeJson<ProcCustomer>();

    if (ProcCustomer != null && !string.IsNullOrWhiteSpace(ProcCustomer.ClientId))
    {
        ProcCustomer.ClientId = HttpUtility.HtmlEncode(ProcCustomer.ClientId);
        
        ProcessValue = ProcCustomer.JsonSerialize(); 
        // or do it directly in the httpClient instead of ProcCustomer.ToString()
        
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Thanks for your suggestion when I say ProcessValue string is used directly meaning they are used as a json string directly as a whole which I modified in my question. Since it's an header so we pass that whole json as an header over the network through http client. I don't extract client_id later on from that ProcessValue json string. So with that in mind how can I make this work? In your first example how can I have modified ProcessValue json string which can be used later on? \$\endgroup\$
    – AndyP
    Nov 12, 2020 at 14:28
  • \$\begingroup\$ You mean to say I can get a string from POCO object directly Then, serialize it whenever needed.? Can you also add an example on how can I get a JSON string from POCO back as well? \$\endgroup\$
    – AndyP
    Nov 12, 2020 at 14:42
  • \$\begingroup\$ @Yes, I'll put a simple example with the POCO. \$\endgroup\$
    – iSR5
    Nov 12, 2020 at 14:43
  • \$\begingroup\$ Cool make sense now. Also my POCO field is like this [DataMember(Name = "client_id")] public string ClientId { get; set; } so should I add [JsonProperty("client_id")] as well on top of that and rest of your code as well? \$\endgroup\$
    – AndyP
    Nov 12, 2020 at 15:08
  • \$\begingroup\$ @AndyP the DataMember would be enough for this situation, so don't add the JsonProperty as both are similar and Json.NET reads both. \$\endgroup\$
    – iSR5
    Nov 12, 2020 at 15:11
1
\$\begingroup\$

If you want to replace a single element in a json then you can take advantage of JObject.

You just need to Parse the json but don't need to Deserialize it.

//Declarations
const string clientIdKey = "client_id";
var ProcessValue = "{\"key1\":\"value1\",\"key2\":\"value2\",\"key3\":\"value3\",\"key4\":\"value4\",\"key5\":\"value6\",\"client_id\":\"Hänzendes\"}";

//Parsing
var jObject = JObject.Parse(ProcessValue);
var clientId = jObject[clientIdKey];

//Replacement
if (clientId != null)
    jObject[clientIdKey] = HttpUtility.HtmlEncode(clientId);

//Saving changes
ProcessValue = jObject.ToString();
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.