C# JSON anonymous serialization - c#

I am trying to serialize some JSON for an API call:
string f5Name = "MyBigIpName";
string poolName = "myPoolName";
string postJson2 = JsonConvert.SerializeObject(
new
{
f5 = new {
f5Name = new {
poolName = memberState
},
}
}
);
This results in the following JSON:
{
"f5": {
"f5Name": {
"poolName": {
"member": {
"address": "10.0.0.0",
"port": 80
},
"session_state": "STATE_DISABLED"
}
}
}
}
However, what I am really looking to do is produce this JSON:
{
"f5": {
"MyBigIpName": {
"myPoolName": {
"member": {
"address": "10.0.0.0",
"port": 80
},
"session_state": "STATE_DISABLED"
}
}
}
}
Is there any way to have the f5Name and poolName property names be dynamic so that I can produce the above JSON? I am using Newtonsoft.JSON (JSON.NET)

I'm not sure if you could do something with a dynamic type or not, but you for sure can do something with Dictionaries:
var obj = new
{
f5 = new Dictionary<string, object>
{
{
f5Name, new Dictionary<string, object> {
{poolName, memberState}
}
}
}
}
string postJson2 = JsonConvert.SerializeObject(obj);

For what you are intending, you'd need to resolve your names in a contract resolver. Something like:
private class MyContractResolver : DefaultContractResolver
{
private Dictionary<string,string> _translate;
public MyContractResolver(Dictionary<string, string> translate)
{
_translate = translate;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
string newPropertyName;
if(_translate.TryGetValue(property.PropertyName, out newPropertyName))
property.PropertyName = newPropertyName;
return property;
}
}
And then:
string f5Name = "MyBigIpName";
string poolName = "MyPoolName";
var _translator = new Dictionary<string, string>()
{
{ "f5Name", f5Name },
{ "poolName", poolName },
};
string postJson2 = JsonConvert.SerializeObject(
new
{
f5 = new {
f5Name = new {
poolName = memberState
},
},
new JsonSerializerSettings { ContractResolver = new MyContractResolver(_translator) });
If you use C# 6, you could benefit from nameof and/or make it cleaner with a dictionary intializer:
var _translator = new Dictionary<string, string>()
{
[nameof(f5Name)] = f5Name ,
[nameof(poolName)] = poolName ,
};
The dictionary creation process could be automated easily too :-)

Related

How to remove unwanted string properties from JSON

Suppose my input is following JSON:
{obj: { x: "", y: "test str" }, myStr: "Hi"}
I would like to remove all empty strings and strings with a value of N/A so that the output becomes:
{obj: { y: "test str" }, myStr: "Hi"}
Note the above input is just a sample input file.
I tried to write some code :
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
But I'm not able to get the desired output.
If you're using a class model to deserialize into, you can use a custom ContractResolver to filter out the unwanted properties on serialization:
class CustomContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var prop = base.CreateProperty(member, memberSerialization);
if (prop.PropertyType == typeof(string) && prop.Readable)
{
prop.ShouldSerialize = obj =>
{
string val = (string)prop.ValueProvider.GetValue(obj);
return !string.IsNullOrEmpty(val) && !val.Equals("N/A", StringComparison.OrdinalIgnoreCase);
};
}
return prop;
}
}
Then use it like this:
var obj = JsonConvert.DeserializeObject<YourClass>(inputJson);
var serializerSettings = new JsonSerializerSettings
{
ContractResolver = new CustomContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy()
}
};
string outputJson = JsonConvert.SerializeObject(obj, serializerSettings);
Working demo here: https://dotnetfiddle.net/zwpj7I
Alternatively, you can remove the unwanted properties without a class model like this:
var jo = JObject.Parse(inputJson);
var unwantedProperties = jo.Descendants()
.OfType<JProperty>()
.Where(p => p.Value.Type == JTokenType.Null ||
(p.Value.Type == JTokenType.String &&
((string)p.Value == string.Empty ||
string.Equals((string)p.Value, "N/A", StringComparison.OrdinalIgnoreCase))))
.ToList();
foreach (var prop in unwantedProperties)
{
prop.Remove();
}
string outputJson = jo.ToString(Formatting.None);
Demo: https://dotnetfiddle.net/QLhDK4
to ignore default empty string using DefaultValueHandling you need to add DefaultValue to x property
[DefaultValue("")]
public string x{ get; set; }
Update:
Here is a link to a working dotnetfiddle
using System;
using Newtonsoft.Json;
using System.ComponentModel;
public class Program
{
public static void Main()
{
//var myClass = new MyClasss();
//var myObj = new Obj();
//myObj.x="";
//myObj.y="test str";
//myClass.myStr = "Hi";
//myClass.obj= myObj;
string json = #"{obj: { x: '', y: 'test str' }, myStr: 'Hi'}";
MyClasss myClass = JsonConvert.DeserializeObject<MyClasss>(json);
var settings = new JsonSerializerSettings();
settings.NullValueHandling = NullValueHandling.Ignore;
settings.DefaultValueHandling = DefaultValueHandling.Ignore;
Console.WriteLine(JsonConvert.SerializeObject(myClass, settings));
}
}
public class Obj {
[DefaultValue("")]
public string x { get; set; }
[DefaultValue("")]
public string y { get; set; }
}
public class MyClasss {
public Obj obj { get; set; }
[DefaultValue("")]
public string myStr { get; set; }
}
Please take a look at ShouldSerialize.
You should declare a method like this in the class containing property "x":
public bool ShouldSerializex()
{
return x != "" && x != "N/A";
}

Unable to parse JSON with multiple JObjects

I am having a string in the following format. I want to assign each JSON within the projects to a separate JObject. When I am trying to parse it and assign accordingly, I am unable to achieve it. How can I parse it?
{
"projects": [
{
"sno": "1",
"project_name": "Abs",
"project_Status": "Live"
},
{
"sno": "2",
"project_name": "Cgi",
"project_Status": "Live"
}
]
}
I have tried the following code;
using (StreamReader streamReader = new StreamReader(Path.Combine(Path.GetTempPath(), "sample.json")))
using (JsonTextReader reader = new JsonTextReader(streamReader))
{
var serializer = new JsonSerializer();
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartObject)
{
JObject jsonPayload = JObject.Load(reader);
jsonProfile = jsonPayload.ToString();
JObject json = JObject.Parse(jsonProfile);
}
}
}
you can use DeserializeAnonymousType as a simpler solution to parse json.
var definition = new
{
projects = new[]{
new {
sno="1",
project_name= "Abs",
project_Status= "Live"
}
}
};
string json1 = #" {
'projects': [
{
'sno': '1',
'project_name': 'Abs',
'project_Status': 'Live'
},
{
'sno': '2',
'project_name': 'Cgi',
'project_Status': 'Live'
}
]
}";
var projects = JsonConvert.DeserializeAnonymousType(json1, definition);
but if you do want to use strong type, you'd better to use JsonProperty
public class Project
{
[JsonProperty("sno")]
public string Sno { get; set; }
[JsonProperty("project_name")]
public string ProjectName { get; set; }
[JsonProperty("project_Status")]
public string ProjectStatus { get; set; }
}
public class JsonModel
{
[JsonProperty("Projects")]
public List<Project> projects { get; set; }
}
JsonConvert.DeserializeObject<JsonModel>(json_Data);

Finding a JSON entry and add a nested element

I have a .json file that looks like this:
[
{
"username": "John",
"currency": 8,
"pulls":
[
{
"character": "person"
},
{
"character": "loved one"
}
]
},
{
"username": "Mike",
"currency": 2,
"pulls":
[
{
"character": "noone"
}
]
},
{
"username": "Clara",
"currency": 5,
"pulls":
[
{
"character": "someone"
}
]
}
]
What I managed to do so far is modify "currency":
bool userExists = false;
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
jsonObject["currency"] += value;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
userExists = true;
}
}
As well as add a completely new entry from scratch:
JsonCollection.User user = new JsonCollection.User();
user.username = username;
user.currency = 10;
using (StreamReader r = new StreamReader(userPath))
{
string json = r.ReadToEnd();
List<JsonCollection.User> users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(json);
users.Add(user);
newJson = JsonConvert.SerializeObject(users, Formatting.Indented);
}
File.WriteAllText(userPath, newJson);
However, no matter what I try I can not add another element to "pulls". The idea is that I call a function with a username and a pull, two strings. Based on the username variable I have to find the corresponding Json Entry and create a new entry within the "pulls" tree based on the pull variable. This is what I could come up with:
public void AddPullToUser(string user, string newPull)
{
user = "Mike"; //test value
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
//jsonObject["pulls"] = newPull;
JsonCollection.Character pull = new JsonCollection.Character();
pull.character = newPull;
jsonObject["pulls"] = pull;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
}
If I do it like this the system can't convert the JsonCollection to the JArray but without using the JArray I don't understand how to find the specific users tree.
In step two this will have to be expanded even further to not create duplicated "pulls", but first of all this has to work in general.
Any help would be greatly appreciated.
Something like this -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JsonConvert.DeserializeObject<List<RootObject>>(json);
var o = obj.FindIndex(a => a.username == "Mike");
obj[o].pulls.AddRange(new List<Pull>{
new Pull{
character = "Modified"
}
});
Console.WriteLine(JsonConvert.SerializeObject(obj));
Where
public class Pull
{
public string character { get; set; }
}
public class RootObject
{
public string username { get; set; }
public int currency { get; set; }
public List<Pull> pulls { get; set; }
}
alternatively, you might be interested in JSON Merge
A possible solution looks like -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JArray.Parse(json);
var idx = obj.IndexOf(obj.FirstOrDefault(a => a["username"].ToString() == "Mike"));
((JArray)obj[idx]["pulls"]).Add(JObject.Parse(#"{
'character': 'new one'
}"));
Console.WriteLine(obj[idx]);
/*output -
{
"username": "Mike",
"currency": 2,
"pulls": [
{
"character": "noone"
},
{
"character": "new one"
}
]
} */
After a bit more research and your help I was able to first of all change all the interaction with Json to the same code-style.
New entry has changed to this:
public void CreateUser(string username)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
users.AddRange(new List<JsonCollection.User> { new JsonCollection.User { username = username, currency = 10, pulls = new List<JsonCollection.Character> { new JsonCollection.Character { character = "TemmieHYPE" } } } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
catch
{
Console.WriteLine("Error on CreateUser");
}
}
Update has changed to this:
public void UpdateUserStats(string username, decimal value, int selection)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int user = users.FindIndex(a => (a.username == username));
if (user != -1)
{
switch (selection)
{
case 1:
users[user].currency += value;
break;
case 2:
users[user].secondsOnline += value;
break;
default:
break;
}
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
//AddPullToUser(username, DateTime.Now.ToString()); //remove on live
}
else
{
CreateUser(username);
}
}
catch
{
Console.WriteLine("Error on UpdateCurrency");
}
}
Most importantly the Add Pull command to add a nested element to the Json now works with the following code:
public void AddPullToUser(string username, string pulledCharacter)
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int alreadyPulled = users.FindIndex(a => (a.username == username) && (a.pulls.FindIndex(b => b.character == pulledCharacter) > 0));
if (alreadyPulled == -1)
{
int user = users.FindIndex(a => (a.username == username));
users[user].pulls.AddRange(new List<JsonCollection.Character> { new JsonCollection.Character { character = pulledCharacter } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
With the addition of the "if (alreadyPulled == -1)" duplicated pulls don't get added to the Json files either.

Deserialize $ref document pointer using JSON.Net

I have been up for hours trying to figure this out. There seem to be a lot of posts tip toe'ing around the issue I am having but none of the posts seem to work for me.
I very simply want to deserialize the $ref field in my json document using json.net
Example JSON:
{
"items": [
{ "$ref": "#/parameters/RequestId" },
{
"name": "user",
"in": "body",
"description": "User metadata object",
"required": true
}
],
"parameters": {
"RequestId": {
"name": "X-requestId",
"in": "header",
"description": "this is a request id",
"required": false
}
}
}
Example Classes:
public class Item
{
public string Name {get;set;}
public string In {get;set;}
public string Description {get;set;}
public bool Required {get;set;}
}
public class RootDoc
{
public List<Item> Items {get;set;}
public Dictionary<string, Item> Parameters {get;set;}
}
What I am expecting:
var doc = JsonConvert.DeserializeObject<RootDoc>(json, new JsonSerializerSettings()
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Error = delegate (object sender, ErrorEventArgs args)
{
// Handle all errors
args.ErrorContext.Handled = true;
}
});
Assert.IsNotNull(doc);
Assert.IsNotNull(doc.Items);
Assert.IsTrue(doc.Items.Count == 2);
Assert.IsTrue(doc.Items[0].Name == "X-requestId");
Assert.IsTrue(doc.Items[1].Name == "user");
EDIT:
I answered the question below. I can't believe I had to do that but it works.
Wow... So here is what I did to solve the problem. I'm not sure if there is an easier way and I can't believe json.net does not provide this out of the box.
var x = JObject.Parse(json);
var xx = x.Descendants().ToList().Where(d => d.Path.ToLower().EndsWith("$ref"));
foreach (var item in xx)
{
if (!item.HasValues)
continue;
string str = item.First.ToString().TrimStart(new char[] { '#' }).TrimStart(new char[] { '/' });
var split = str.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
JToken token = x;
for (int i = 0;i<split.Length; i++)
{
token = token[split[i]];
}
item.Parent.Replace(token);
}
var doc = JsonConvert.DeserializeObject<RootDoc>(x.ToString(), new JsonSerializerSettings()
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
// Handle all errors
args.ErrorContext.Handled = true;
}
});
Assert.IsNotNull(doc);
Assert.IsNotNull(doc.Items);
Assert.IsTrue(doc.Items.Count == 2);
Assert.IsTrue(doc.Items[0].Name == "X-requestId");
Assert.IsTrue(doc.Items[1].Name == "user");
You can use this code.
JObject o = JObject.Parse(json);
JToken tokenToReplace = o.SelectToken("$..$ref");
string[] s = tokenToReplace.Value<string>().Split('/');
string a = s[s.Length - 1];
JToken newToken = o.SelectToken("$.." + a);
JArray items = (JArray)o["items"];
items.Add(newToken);
tokenToReplace.Parent.Parent.Remove();
string newJson = o.ToString();
newJson contains replaced $ref.

Serialize object with json.net with nested dictionaries

I'm a little lost with serializing a JSON object, I'm trying to serialize the Item class into a JSON
class Item
{
public Dictionary<string, object> components = new Dictionary<string, object> { };
public string mixins { get; set; }
public string type { get; set; }
}
class Components
{
public Dictionary<string, object> unit_info = new Dictionary<string, object> { };
public Dictionary<object, object> generateUnitInfo()
{
unit_info.Add("bla", "blubb");
unit_info.Add("blubb", "bla");
return unit_info;
}
}
My JSON should look like this
{
"components": {
"unit_info" : {
"bla": "blubb",
"blubb": "bla",
},
}
}
Any hint would be helpful, thanks in advance
EDIT: thats the code that I have so far
Component c = new Component();
Item item = new Item();
item.type = CBItemType.SelectedItem.ToString();
item.mixins = "test mixins";
item.components.Add(c.unit_info, c.generateUnitInfo());
string json = JsonConvert.SerializeObject(item, Formatting.Indented);
and thats what I get
{
"mixins": "test mixins",
"type": "entity",
"components": {
"(Collection)": {
"bla": "blubb",
"blubb": "bla"
}
},
"entity_data": {}
}
The generateUnitInfo method adds 2 k/v pairs to the unit_info, I want instead of (Collection) unit_info
You can use anonymous objects (Json.Net)
var obj = new { components = new { unit_info = new { bla="blubb", blubb="bla" } } };
var json = JsonConvert.SerializeObject(obj);
Built-in JavaScriptSerializer would give the same result too
var json = new JavaScriptSerializer().Serialize(obj);
PS: If using dictionary is a must you can use it too
var obj = new Dictionary<string, object>()
{
{
"components", new Dictionary<string,object>()
{
{
"unit_info" , new Dictionary<string,object>()
{
{ "bla", "blubb" }, {"blubb", "bla" }
}
}
}
}
};
Both serializers will return your expected json.

Categories

Resources