Upper case JSON keys - c#

Let's say I have a JSON based string that looks: {"Hello":"1"} I want to convert it to look like {"HELLO":"1"}.
I have created an upper case naming strategy:
public class UpperCaseNamingStrategy : NamingStrategy
{
protected override string ResolvePropertyName(string name)
{
return name.ToUpper();
}
}
Which works when manipulating an object:
[Fact]
public void TestUpperCase()
{
var thing = new {Hello = "1"};
var jsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new UpperCaseNamingStrategy { OverrideSpecifiedNames = true }
}
};
var serializeObject = JsonConvert.SerializeObject(thing, jsonSerializerSettings);
Assert.Equal("{\"HELLO\":\"1\"}", serializeObject); // Works fine
}
But when I load in a string via the JObject.Parse API, it seems to leave the string as is:
[Fact]
public void TestUpperCase2()
{
var thing = JObject.Parse("{\"Hello\":\"1\"}");
var jsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new UpperCaseNamingStrategy { OverrideSpecifiedNames = true }
}
};
var serializeObject = JsonConvert.SerializeObject(thing, jsonSerializerSettings);
Assert.Equal("{\"HELLO\":\"1\"}", serializeObject); // this fails Actual: {"Hello":"1"}
}
In this test I am simulating my use case, I am retrieving a JSON response from a REST API, I am trying to take that response string and convert all of the keys into upper case string.
Doing a little debugging I can see that the object that is getting loaded into the JObject looks something like {{"Hello": "1"}}.
I just want to point out that the data I am using is much more complex than just simple "hello" I just wanted a quick example, let's say I have 20 fields some being objects some being arrays, is there an easy way I can parse the JSON and have all the keys to use upper case naming.

The way I've decided to solve this problem is to create a POCO (plain old c object) to store the information from the API response:
public class Poco
{
public string Hello {get;set;}
}
When I want to upper case all of the property keys send it through the serialization with my Upper case naming strategy:
var responseModel = JsonConvert.DeserializeObject<TResponse>(data);
return JObject.Parse(JsonConvert.SerializeObject(responseModel,
new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore,
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new UpperCaseNamingStrategy
{
OverrideSpecifiedNames = true
}
}
}));
I wish there was a more generic way of doing this, but this seems like a sensible solution

So in case someone still tries to figure that out:
Declare a naming policy
public class UpperCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) => name.ToUpper();
}
And in your controller:
using System.Text.Json;
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = new UpperCaseNamingPolicy(),
WriteIndented = true
};
return Json(result, options);

Related

convert json response to dynamic specific object in .net C#

I'm working on .NET Core where I'm trying to masking some sensitive data like emailid, password, etc.. I want all my json response data keys or properties name should be in lowercase for that I'm using one of the feature of Newtonsoft e.g ContractResolver which helps us to customize property name.
But it seems, it only works with generic class type e.g
class customer
{
public string firstName { get; set; }
}
but it not working when we have json string e.g
{\n \n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n}
now i'm looking for how to convert json response to specific object type dynamically so that i can pass that object to contractResolver checkout below code.
public class LowerCaseResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToLower();
}
}
using Newtonsoft.Json;
using SerializationContractResolverExample.ContractResolver;
using System;
namespace SerializationContractResolverExample
{
class Program
{
static void Main(string[] args)
{
CustomerInfo customerInfo = new CustomerInfo()
{
FirstName = "Sukhpinder",
LastName = "Singh",
MobileNumbers = new System.Collections.Generic.List<string>() {
"33443434343"
}
};
var sestting = new JsonSerializerSettings
{
ContractResolver = new LowerCaseResolver()
};
// need to convert below response to dynamic generic type
string tem1 = "{\n \"lmsUserId\": 10268,\n \"landlordProfileId\": \"2ea81674-6ca6-478c-a9c6-fefbe9572f28\",\n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n \"email\": \"yesteluydi#vusra.com\",\n \"createdBy\": 1551,\n \"createdDate\": \"2022-05-05T17:05:10.37\",\n \"user\": null,\n \"linkedLLCs\": null,\n \"ssn\": null,\n \"accountTypeId\": 2,\n \"completeLater\": false\n}";
var responseLowerCase = JsonConvert.SerializeObject(tem1, Formatting.Indented, sestting); //Not working
var responseLowerCase = JsonConvert.SerializeObject(customerInfo, Formatting.Indented, sestting); //Working Fine
Console.WriteLine(responseLowerCase);
Console.ReadLine();
}
}
}
You can try to use JsonConvert.DeserializeObject to convert tem1 to Object .
string tem1 = "{\n \"lmsUserId\": 10268,\n \"landlordProfileId\": \"2ea81674-6ca6-478c-a9c6-fefbe9572f28\",\n \"firstName\": \"Testbusiness\",\n \"lastName\": \"business\",\n \"email\": \"yesteluydi#vusra.com\",\n \"createdBy\": 1551,\n \"createdDate\": \"2022-05-05T17:05:10.37\",\n \"user\": null,\n \"linkedLLCs\": null,\n \"ssn\": null,\n \"accountTypeId\": 2,\n \"completeLater\": false\n}";
var s = JsonConvert.DeserializeObject<Object>(tem1);
result:

Deserialize class with an array of base objects into class with an array of inherited objects (Newtonsoft.Json)

I've been working on a music game and decided to add convertation of other games' levels. One of the games I decided to convert from uses JSON to store it's levels and so I'm using Newtonsoft.Json for deserializing level data. Level have can 2 object types that are stored in a single array/list with one shared property and one individual property. Keeping that in mind I made level class with it's properties, one base and two inherited classes:
class Chart
{
//Some unimportant properties
//...
public Note[] note;
class Note
{
public int[] beat;
}
class NoteSingle : Note
{
public int x;
}
class NoteRain : Note
{
public int[] endbeat;
}
}
However, when I try deserialize level, note only contains base objects. I tried creating JsonSerializerSettings with TypeNameHandling set to All and passing it to deserialization method, but it didn't worked, note still only have base classes in it.
Basically I need to load level from the file, deserialize it as Chart and make each of the notes in note be one of the types inherited from Note depending on json data. Like if note has x field then load it as NoteSingle and if it has endbeat field then load it as NoteRain.
Repres
class Note
{
public int[] beat;
}
class NoteSingle : Note
{
public int x;
}
class NoteRain : Note
{
public int[] endbeat;
}
class Chart
{
//Some unimportant properties
public Note[] note;
//Some unimportant properties
}
public static void Convert(string path)
{
string rawData = File.ReadAllText(path);
JsonSerializerSettings setts = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All
};
Chart ch = JsonConvert.DeserializeObject<Chart>(rawData, setts);
//Level converter code
}
Example data I'm trying to load: https://pastebin.com/zgnRsgWZ
What am I doing wrong?
I try with this code:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
};
var chart = new Chart();
chart.note = new Note[]
{
new NoteSingle { x = 37 },
new NoteRain { endbeat = new[] { 9 } }
};
var json = JsonConvert.SerializeObject(chart, settings);
var chart2 = JsonConvert.DeserializeObject<Chart>(json, settings);
And it's working. json has this value:
{
"$type":"Test.Chart, SoApp",
"note":{
"$type":"Test.Chart+Note[], SoApp",
"$values":[
{
"$type":"Test.Chart+NoteSingle, SoApp",
"x":37,
"beat":null
},
{
"$type":"Test.Chart+NoteRain, SoApp",
"endbeat":{
"$type":"System.Int32[], mscorlib",
"$values":[
9
]
},
"beat":null
}
]
}
}
And chart2 has 2 notes of NoteSingle and NoteRain types. Maybe you aren't using TypeNameHandling.All in Serialize. You need to use both on Serialize and Deserialize.
UPDATE
If you haven't control of the generated JSON, you can use a Converter to deserialize it:
public class YourJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var chart = new Chart();
var notes = new List<Note>();
string name = null;
NoteRain noteRain = null;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonToken.PropertyName:
name = reader.Value.ToString();
break;
case JsonToken.Integer:
if (name == "x")
{
var note = new NoteSingle { x = Convert.ToInt32(reader.Value) };
notes.Add(note);
}
else if (name == "endbeat")
{
if (noteRain == null)
{
noteRain = new NoteRain { endbeat = new[] { Convert.ToInt32(reader.Value) } };
notes.Add(noteRain);
}
else
{
var array = noteRain.endbeat;
noteRain.endbeat = new int[noteRain.endbeat.Length + 1];
for (int i = 0; i < array.Length; i++)
{
noteRain.endbeat[i] = array[i];
}
noteRain.endbeat[noteRain.endbeat.Length - 1] = Convert.ToInt32(reader.Value);
}
}
break;
}
}
chart.note = notes.ToArray();
return chart;
}
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return true;
}
}
This is a simple example, you must tune it but I think it's easy to do. In the property name I get the name of the property and I use later to create the correct type. If I process a x property I know that is a NoteSingle and I create it and add to notes list.
If, for example, you get a property name like beat and you don't know yet the type of the Note class, use a temporary variable to store and fill and later, when you read a property that you know is from a concrete class, create the Note instance and fill with this variable. And after that, If you read more data of this class, continue filling your instance.
Usage:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new YourJsonConverter());
var chart = JsonConvert.DeserializeObject<Chart>(json, settings);

How to show a property returns a copy (i.e. is immutable)

Take the following snippet as an example:
public class JsonHelper
{
private readonly JsonSerializerSettings _serializerSettings;
public JsonSerializerSettings Settings
{
get //Settings of JsonHelper should be immutable, that's why 'get' does not return the field _serializerSettings
{
return new JsonSerializerSettings()
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
};
}
}
public JsonHelper()
{
_serializerSettings = Settings;
}
}
I don't want the JsonSerializerSettings to be changeable from the outside but I want to be able to offer these same settings in scenarios where I can't directly use JsonHelper.
Is there any way to signal to a user of this class that he can't do the following and expect the result s/he's expecting?
var instance = new JsonHelper();
instance.Settings.DateTimeZoneHandling = DateTimeZoneHandling.Local;
Or is the only way to create a method called GetCopyOfSettings()?

WebAPI Temporarily Override JsonFormatter from OnActionExecuted

I'm trying to create an attribute that will serialize data return from an action differently
public override void OnActionExecuted(HttpActionExecutedContext filterContext)
{
var content = (filterContext.Response.Content as ObjectContent);
if (content == null)
{
return;
}
if (content.ObjectType.IsGenericType
&& content.ObjectType.GetGenericTypeDefinition() == typeof (Page<>))
{
var pageObject = (content.Value as IPage);
var jsonFormatterRule = new JsonFormatterRule();
var pageJson = JsonConvert.SerializeObject(pageObject.ItemsArray,
jsonFormatterRule.GetPascalCasedSettings());
//How do I set the content that \/ doesn't compile?
//filterContext.Response.Content = pageJson;
}
}
This is the JsonFormatterRules incase anyone wanted to see them.
public JsonSerializerSettings GetDefaultSettings()
{
var settings = new JsonSerializerSettings()
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind,
};
settings.Converters.AddRange(defaultConfiguredConverters);
return settings;
}
public JsonSerializerSettings GetPascalCasedSettings()
{
var settings = this.GetDefaultSettings();
settings.ContractResolver = new DefaultContractResolver();
return settings;
}
How can I Set the Content From On Action Executed? I cannot change the default serializer to the DefaultContract Globally because it could threading issues.
Also I'd prefer not to have to create a new response and copy over the Headers from the old one that seems like over kill.
One way to do this would be to define a custom formatter.
First, define your attribute:
[AttributeUsage(AttributeTargets.Class)]
public sealed class SpecialSerializeAttribute : Attribute
{
}
Now create a formatter that will find the attribute:
public class SpecialSerializeFormatter : MediaTypeFormatter
{
public SpecialSerializeFormatter()
{
//You can add any other supported types here.
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
}
public override bool CanReadType(Type type)
{
//you can just return false if you don't want to read any differently than your default way
//if you return true here, you should override the ReadFromStreamAsync method to do custom deserialize
return type.IsDefined(typeof(SpecialSerializeAttribute), true));
}
public override bool CanWriteType(Type type)
{
return type.IsDefined(typeof(SpecialSerializeAttribute), true));
}
public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content,
TransportContext transportContext)
{
//value will be your object that you want to serialize
//add any custom serialize settings here
var json = JsonConvert.SerializeObject(value);
//Use the right encoding for your application here
var byteArray = Encoding.UTF8.GetBytes(json);
await writeStream.WriteAsync(byteArray, 0, byteArray.Length);
}
}
Register the formatter in you WebApiConfig.cs
You can also build a formatter for each type directly and then you don't have to do the Attribute. Just change your CanRead and CanWrite methods. I find basing these of the direct Type's gives better results since it's not such a generic formatter and you may need to apply custom logic based on the type, but the above answer should get you what you need.
Incase anyone was wondering, Response Content is an HTTPContent, which inherits from ByteArrayContent. So if you have your JSON Serialized already, all you have to do is put it into a byte array.
filterContext.ActionContext.Response.Content = new ByteArrayContent(Encoding.ASCII.GetBytes(pageJson));

Return json with lower case first letter of property names

I have LoginModel:
public class LoginModel : IData
{
public string Email { get; set; }
public string Password { get; set; }
}
and I have the Web api method
public IHttpActionResult Login([FromBody] LoginModel model)
{
return this.Ok(model);
}
And it's return 200 and body:
{
Email: "dfdf",
Password: "dsfsdf"
}
But I want to get with lower first letter in property like
{
email: "dfdf",
password: "dsfsdf"
}
And I have Json contract resolver for correcting
public class FirstLowerContractResolver : DefaultContractResolver
{
protected override string ResolvePropertyName(string propertyName)
{
if (string.IsNullOrWhiteSpace(propertyName))
return string.Empty;
return $"{char.ToLower(propertyName[0])}{propertyName.Substring(1)}";
}
}
How I can apply this?
If your are using Newtonsoft.Json, you can add JsonProperties to your view model :
public class LoginModel : IData
{
[JsonProperty(PropertyName = "email")]
public string Email {get;set;}
[JsonProperty(PropertyName = "password")]
public string Password {get;set;}
}
To force all json data returned from api to camel case it's easier to use Newtonsoft Json with the default camel case contract resolver.
Create a class like this one:
using Newtonsoft.Json.Serialization;
internal class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
_jsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
return new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
}
}
and set this during api configuration (at startup):
var jsonFormatter = new JsonMediaTypeFormatter();
httpConfiguration.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
You can add the two following statement in the configuration of the web API or to the startup file
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented;
But it is very important to use the return Ok() method instead of return Json() otherwise; this will not work.
if you have to use Json method (and have no other choice)
then see this answer https://stackoverflow.com/a/28960505/4390133
If you need it only in some certain place and not throughout whole application, then you can do following:
var objectToSerialize = new {Property1 = "value1", SubOjbect = new { SubObjectId = 1 }};
var json = Newtonsoft.Json.JsonConvert.SerializeObject(data, new JsonSerializerSettings { ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() });
It should result in {"property1":"value1","subOjbect":{"subObjectId":1}} (note that nested properties also starts from lowercase)

Categories

Resources