Json.net JsonIgnore doesn't work for nested classes - c#

I am trying to serialize some third party json using Json.net and the problem is that they started sending Ids as strings insted of Guids. So I am trying to ignore the Ids within serialization but there seems to be a problem within nested properties that the JsonIgnore doesn't work. For that reason I decided to add my own Ids after but the serialisation itself doesn't seem to ignore what I need it to.
My classes used for serialization:
public class ItemA: General.Common
{
[JsonIgnore]
public new Guid Id { get; set; } //hiding Guid Id Common
public Folder ParentFolder { get; set; }
//...
}
public class Folder
{
public string Id { get; set; }
public string Path { get; set; }
}
public class ItemB: NotImportant
{
//...
public List<Folder> Folders { get; set; } = new List<Folder>();
public List<ItemA> ItemAs{ get; set; } = new List<ItemA>();
}
My Code:
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
string json = "some Json-that includes some Ids as strings";
dynamic d = JsonConvert.DeserializeObject<ItemB>(json, jsonSettings);
//serialization error here that cannot convert string to Guid in ItemAs
((ItemB)d).ItemAs.ForEach(x => x.Id = new Guid());
EDIT:
The error is something like this:
Error converting value "RgAAAAAD01CCe0GCRpDdKTQq2OCQBwAIuTruAfDrRZi9RPZnww3OAAAAAAEMAAAIuTruAfDrRZi9RPZnww3OAABE1hqaAAAA" to type 'System.Guid'...

There is an official Issue about this:
Issue #463 - JsonIgnore attribute on shadowed properties
They are separate properties, one happens to be hiding the other. By ignoring the one on Derived then the property on base is no longer hidden and is being serialized instead. If you want to ignore both then place [JsonIgnore] on both, or if you want [JsonIgnore] on the Derived class to ignore both then base the property virtual and override it on Derived. - JamesNK

It's possible to ignore during deserialize : fully working example based on your code: (see Ignore a property when deserializing using Json.Net with ItemRequired = Required.Always)
using System;
using System.IO;
using System.Collections.Generic;
using Newtonsoft.Json;
[JsonObject(ItemRequired = Required.Always)]
public class ItemA : General.Common
{
[JsonIgnore]
[JsonProperty(Required = Required.Default)]
public new Guid Id { get; set; } //hiding Guid Id Common
public Folder ParentFolder { get; set; }
//...
}
public class Folder
{
public string Id { get; set; }
public string Path { get; set; }
}
public class ItemB : NotImportant
{
//...
public List<Folder> Folders { get; set; } = new List<Folder>();
public List<ItemA> ItemAs { get; set; } = new List<ItemA>();
}
public class Test
{
var jsonSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
//ItemB b = new ItemB()
//{
// Folders = new List<Folder>() {
// new Folder() { Id = "1", Path = "myPath1" },
// new Folder() { Id = "2", Path = "myPath2" },
// new Folder() { Id = "3", Path = "myPath3" } },
// ItemAs = new List<ItemA>() {
// new ItemA() { Id = Guid.NewGuid(), ParentFolder = new Folder()
// { Id = "p1", Path = "parentpath1" } },
//new ItemA()
//{ Id = Guid.NewGuid(),
// ParentFolder = new Folder()
//{ Id = "p2", Path = "parentpath2" } }}
//};
//string json = JsonConvert.SerializeObject(b);
string json = "{\"Folders\":[{\"Id\":\"1\",\"Path\":\"myPath1\"},{\"Id\":\"2\",\"Path\":\"myPath2\"},{\"Id\":\"3\",\"Path\":\"myPath3\"}],\"ItemAs\":[{\"Id\":\"RgAAAAAD01CCe0GCRpDdKTQq2OCQBwAIuTruAfDrRZi9RPZnww3OAAAAAAEMAAAIuTruAfDrRZi9RPZnww3OAABE1hqaAAAA\",\"ParentFolder\":{\"Id\":\"p1\",\"Path\":\"parentpath1\"}},{\"Id\":\"c0af33a9-3e6f-4405-a2d4-ff469cb67fce\",\"ParentFolder\":{\"Id\":\"p2\",\"Path\":\"parentpath2\"}}]}";
dynamic d = JsonConvert.DeserializeObject<ItemB>(json, jsonSettings);
//no serialization error
((ItemB)d).ItemAs.ForEach(x => x.Id = Guid.NewGuid());
}

Related

Deserialising XML with different element name in c#

XML:
<?xml version="1.0" encoding="UTF-8"?>
<Images>
<I0>
<Path>123.com</Path>
<I0>
<I1>
<Path>123.com</Path>
<I1>
<I2>
<Path>123.com</Path>
<I2>
</Images>
Can serializer.Deserialize() be used to get tags with different names into a collection?
currently, in my object I have:
C#:
public class rootObject
{
[XmlElement(ElementName = "I0")]
public I0 I0 { get; set; }
[XmlElement(ElementName = "I1")]
public I1 I1 { get; set; }
[XmlElement(ElementName = "I2")]
public I2 I2 { get; set; }
}
But I would like to have (Because Images can have more or fewer elements):
public class rootObject
{
public List<I> Is { get; set; }
}
You can do what you are suggesting you just merely need to pass in the type argument in your class doing the generic. The key point to remember when you do a deserialization routine is that the routine needs to know the sub reference. So if I was to say string.Deserialize it would bomb. It would need to know a reference string.Deserialize> where Sub could be the class object that may change.
Say I have a base class and I want 'T' to be a type I can change for extensible abilities later.
[Serializable]
public class Test<T> where T : class
{
public Test() { }
public int TestId { get; set; }
public string Name { get; set; }
public List<T> Shipments { get; set; }
}
I want to test this with two classes I just make up that have different properties slightly
[Serializable]
public class Sub1
{
public int Id { get; set; }
public string Desc { get; set; }
}
[Serializable]
public class Sub2
{
public int IdWhatever { get; set; }
public string DescWhatever { get; set; }
}
Now let's do a main program and test serialization.
class Program
{
static void Main(string[] args)
{
var serializeTest = new Test<Sub1> { TestId = 1, Name = "Test", Shipments = new List<Sub1> { new Sub1 { Id = 1, Desc = "Test" }, new Sub1 { Id = 2, Desc = "Test2" } } };
var serializeTest2 = new Test<Sub2> { TestId = 1, Name = "Test", Shipments = new List<Sub2> { new Sub2 { IdWhatever = 1, DescWhatever = "Test" }, new Sub2 { IdWhatever = 2, DescWhatever = "Test2" } } };
var serialized = serializeTest.SerializeToXml();
var serialized2 = serializeTest2.SerializeToXml();
var deserialized = serialized.DeserializeXml<Test<Sub1>>();
var deserialized2 = serialized2.DeserializeXml<Test<Sub2>>();
Console.WriteLine(serialized);
Console.WriteLine();
Console.WriteLine(serialized2);
Console.ReadLine();
}
}
And my Serialize and DeSerialize extension methods:
public static string SerializeToXml<T>(this T valueToSerialize, string namespaceUsed = null)
{
var ns = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, (namespaceUsed != null) ? namespaceUsed : string.Empty) });
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(typeof(T));
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
You don't need to specify the XmlElement name when the properties match the XML. A few solutions, some kinda hacky :).
HACKY: use regex string replace to replace <I#> and </I#> to
just <I> and </I>
SOMEWHAT HACKY: This might work for you:
How to deserialize an XML array containing multiple types of elements in C#,
but you'd have to add an attribute for i0, i1 ... i100, etc.
BEST: Is that your entire XML? I'd honestly just use LINQToXml and
do a Descendants("Path") and get an array of strings back with 1 line of code. Serialization is not really the best solution for this.

Unable to deserialize JSON array

Below is my class :
public class Employee : Base
{
public int Id { get; set; }
public string Fname { get; set; }
public DepartmentModel Department { get; set; }
}
public class DepartmentModel : Base
{
public int Id { get; set; }
public string DepartmentName { get; set; }
public List<Location> Locations { get; set; }
}
public class Locations
{
public string Area { get; set; }
public string StreetNo { get; set; }
public string Nearby { get; set; }
}
Response return from service:
var response = new
{
id = 100,
department = new
{
id = 200,
departmentName = "Abc",
locations = new[]
{
Employee.Department.Locations
.Select
(
lo => new
{
area = lo.Area,
streetNo = lo.streetNo,
nearby = lo.Nearby
}
).ToList()
}
}
};
return JsonConvert.SerializeObject(response);
Now when I try to deserialize this above JSON into my class Employee like below:
var deserialize = JsonConvert.DeserializeObject<Employee>(response.ToString());
Error:
How can I deserialize this above JSON?
The problem lies here:
locations = new[]
{
Employee.Department.Locations
.Select
(
lo => new
{
area = lo.Area,
streetNo = lo.streetNo,
nearby = lo.Nearby
}
).ToList()
}
The LINQ expression ends with .ToList() and thus is already returning a list of items. You are then wrapping that with new[] in an array. So, instead of being an array of Locations, the JSON is an array of an array of Locations.
Try removing the new[]. You don't want locations to be an array of lists
locations = Employee.Department.Locations
.Select(lo => new
{
area = lo.Area,
streetNo = lo.streetNo,
nearby = lo.Nearby
}
).ToList()
You need to instantiate a new Employee() and use the same casing as the classes:
var response = new Employee() // Instantiates Employee to ensure correct properties used.
{
Id = 100, // Has PascalCase.
Department = new DepartmentModel()
{
Id = 200,
DepartmentName = "Abc",
Locations = Employee.Department.Locations
.Select(lo => new Location
{
Area = lo.Area,
StreetNo = lo.StreetNo,
Nearby = lo.Nearby
}
).ToList()
}
};

cant figure out how to map this json into C# classes

So I have the json below that I want to Deseralize into Classes so I can work with it. But the issues is that the top two fields are a different type to all the rest
"items": {
"averageItemLevel": 718,
"averageItemLevelEquipped": 716,
"head": { ... },
"chest": { ... },
"feet": { ... },
"hands": { ... }
}
Where ... is a the Item class below, but the problem is that 2 of the fields are ints and the rest are Item, there are about 20 fields in total. So what I'd like to do is put them into a Dictionary<string, Item> but the 2 int fields are preventing me from Deseralizing it into that. I'm using JavaScriptSerializer.Deserialize<T>() to do this.
I could have each item as it's own class with the name of the item as the name of the class, but I find that to be very bad, repeating so much each time, also very hard to work with later since I cant iterate over the fields, where as I could a Dictionary. Any idea how I could overcome this?
public class Item
{
public ItemDetails itemDetails { get; set; }
public int id { get; set; }
public string name { get; set; }
public string icon { get; set; }
public int quality { get; set; }
public int itemLevel { get; set; }
public TooltipParams tooltipParams { get; set; }
public List<Stat> stats { get; set; }
public int armor { get; set; }
public string context { get; set; }
public List<int> bonusLists { get; set; }
}
Update: from the comments I came up with this solution
JObject jsonObject = JObject.Parse(json);
jsonObject["averageItemLevel"] = int.Parse(jsonObject["items"]["averageItemLevel"].ToString());
jsonObject["averageItemLevelEquipped"] = int.Parse(jsonObject["items"]["averageItemLevelEquipped"].ToString());
jsonObject["items"]["averageItemLevel"].Parent.Remove();
jsonObject["items"]["averageItemLevelEquipped"].Parent.Remove();
var finalJson = jsonObject.ToString(Newtonsoft.Json.Formatting.None);
var character = _serializer.Deserialize<Character>(finalJson);
character.progression.raids.RemoveAll(x => x.name != "My House");
return character
If I add these two classes to match your JSON I can serialize and deserialize the objects:
public class root
{
public Items items { get; set; }
}
public class Items
{
public int averageItemLevel { get; set; }
public int averageItemLevelEquipped { get; set; }
public Item head {get;set;}
public Item chest {get;set;}
public Item feet {get;set;}
public Item hands {get;set;}
}
Test rig with the WCF Serializer:
var obj = new root();
obj.items = new Items
{
averageItemLevel = 42,
feet = new Item { armor = 4242 },
chest = new Item { name = "super chest" }
};
var ser = new DataContractJsonSerializer(typeof(root));
using (var ms = new MemoryStream())
{
ser.WriteObject(ms, obj);
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
Console.WriteLine("and deserialize");
ms.Position = 0;
var deserializeObject = (root) ser.ReadObject(ms);
Console.WriteLine(deserializeObject.items.feet.armor);
}
And with the JavaScriptSerializer:
var jsser = new JavaScriptSerializer();
var json = jsser.Serialize(obj);
Console.WriteLine(json);
Console.WriteLine("and deserialize");
var djson = jsser.Deserialize<root>(json);
Console.WriteLine(djson.items.feet.armor);
Both serializers give the same result for your given JSON.

How to Create multi level Json using Jobject in C#?

I want to create multi level Json, Using http://json2csharp.com/. I created classes. But not sure how to use it.
public class MassPay
{
public string legal_name { get; set; }
public string account_number { get; set; }
public string routing_number { get; set; }
public string amount { get; set; }
public string trans_type { get; set; }
public string account_class { get; set; }
public string account_type { get; set; }
public string status_url { get; set; }
public string supp_id { get; set; }
public string user_info { get; set; }
}
public class MassPayList
{
public string oauth_consumer_key { get; set; }
public string bank_id { get; set; }
public string facilitator_fee { get; set; }
public IList<MassPay> mass_pays { get; set; }
}
These are my classes and this is Json Format i want to create...
there are extra elements...
{
"oauth_consumer_key":"some_oauth_token",
"mass_pays":[
{"legal_name":"SomePerson1",
"account_number":"888888888",
"routing_number":"222222222",
"amount":"10.33",
"trans_type":"0",
"account_class":"1",
"account_type":"2"
},
{"legal_name":"SomePerson2",
"account_number":"888888888",
"routing_number":"222222222",
"amount":"10.33",
"trans_type":"0",
"account_class":"1",
"account_type":"1"}
]
}
So far i have come up with below code..I am using JObject, and all others wer single level so it was pretty easy. but when it comes to two or three level its difficult.
public JObject AddMassPayRequest(MassPayList lMassPayList, MassPay lMassPay)
{
JObject pin = new JObject(
new JProperty("legal_name", lMassPay.legal_name),
new JProperty("account_number", lMassPay.account_number),
new JProperty("routing_number", lMassPay.routing_number),
new JProperty("amount", lMassPay.amount),
new JProperty("trans_type", lMassPay.trans_type),
new JProperty("account_class", lMassPay.account_class),
new JProperty("account_type", lMassPay.account_type),
new JProperty("status_url", lMassPay.status_url),
new JProperty("supp_id", lMassPay.supp_id),
new JProperty("status_url", lMassPay.status_url),
new JProperty("user_info", lMassPay.user_info)
);
return pin;
}
public JObject AddMassPayRequestList(MassPayList lMassPayList, MassPay lMassPay)
{
JObject pin = new JObject(
new JProperty("mass_pays", lMassPayList.mass_pays),
new JProperty("bank_id", lMassPayList.bank_id),
new JProperty("facilitator_fee", lMassPayList.facilitator_fee),
new JProperty("oauth_consumer_key", lMassPayList.oauth_consumer_key)
);
return pin;
}
Can some one help me how to do this..?
if you're using ASP.NET MVC you just need to use the Json response action using your existing classes.
You could simply do something like this in a controller:
return Json(new { PoId = newPoId, Success = true });
or an actual concrete model class:
var _AddMassPayRequestList = new AddMassPayRequestList();
...
returning a populated instance of your AddMassPayRequestList class:
return Json(_AddMassPayRequestList);
So finally I got this answer, Its simple structure. Using this u can create any type of Json... It doesnt have to follow same structure..
The logic behind this is add things you want at start, create class and inside that properties you want to add into json. SO while passign just add for loop and pass Object to the list.. It will loop through and create JSon for You..
If you have any doubts, let me know happy to help you
public String ToJSONRepresentation(List<MassPay> lMassPay)
{
StringBuilder sb = new StringBuilder();
JsonWriter jw = new JsonTextWriter(new StringWriter(sb));
jw.Formatting = Formatting.Indented;
jw.WriteStartObject();
jw.WritePropertyName("oauth_consumer_key");
jw.WriteValue("asdasdsadasdas");
jw.WritePropertyName("mass_pays");
jw.WriteStartArray();
int i;
i = 0;
for (i = 0; i < lMassPay.Count; i++)
{
jw.WriteStartObject();
jw.WritePropertyName("legal_name");
jw.WriteValue(lMassPay[i].legal_name);
jw.WritePropertyName("account_number");
jw.WriteValue(lMassPay[i].account_number);
jw.WritePropertyName("routing_number");
jw.WriteValue(lMassPay[i].routing_number);
jw.WritePropertyName("amount");
jw.WriteValue(lMassPay[i].amount);
jw.WritePropertyName("trans_type");
jw.WriteValue(lMassPay[i].trans_type);
jw.WritePropertyName("account_class");
jw.WriteValue(lMassPay[i].account_class);
jw.WritePropertyName("account_type");
jw.WriteValue(lMassPay[i].account_type);
jw.WritePropertyName("status_url");
jw.WriteValue(lMassPay[i].status_url);
jw.WritePropertyName("supp_id");
jw.WriteValue(lMassPay[i].supp_id);
jw.WriteEndObject();
}
jw.WriteEndArray();
jw.WriteEndObject();
return sb.ToString();
}

Noob using C# classes and accessing them

I'm working with JSON (using json.net) and a C# console application and I am trying to set some values for a JSON POST to a server.
I can set some of the values, but accessing others is giving me fits.
My JSON looks like this:
{
"params" : [
{
"url" : "sys/login/user",
"data" : [
{
"passwd" : "pwd",
"user" : "user"
}
]
}
],
"session" : 1,
"id" : 1,
"method" : "exec"
}
I ran that through json2csharp and it generated me the following classes.
public class Datum
{
public string passwd { get; set; }
public string user { get; set; }
}
public class Param
{
public string url { get; set; }
public List<Datum> data { get; set; }
}
public class RootObject
{
public List<Param> #params { get; set; }
public string session { get; set; }
public int id { get; set; }
public string method { get; set; }
}
I then created this object for testing in my Main method
RootObject temp = new RootObject()
temp.id = 1;
temp.method = "exec";
temp.session = "1";
and those parameters get set just fine.
I can also set the URL param using the following:
temp.#params.Add(new Param { url = "some/url", });
It is setting the public List<Datum> data { get; set; } item that is the problem. I cannot figure out how to access that and set the user and password items.
If I add this to the Param class I can set the values, but this seems to be the wrong way/place to me.
public Param()
{
data = new List<Datum>();
data.Add(new Datum { user = "user", passwd = "pass" });
}
Well, you create your RootObject like this:
RootObject temp = new RootObject()
temp.id = 1;
temp.method = "exec";
temp.session = "1";
Then you create the params list and fill it with one Param:
temp.#params = new List<Param>();
temp.#params.Add(new Param { url = "some/url" });
You can then set the data for one param in the list (in this example the first one):
temp.#params[0].data = new List<Datum>();
temp.#params[0].data.Add(new Datum { user = "user", passwd = "pass" });
This is necessary, because #params is a list of Param objects. You could also fill the data when creating the Param instance before adding it to the list (easier, because you otherwise need to know the list index).
temp.#params = new List<Param>();
Param p = new Param { url = "some/url" };
p.data = new List<Datum>();
p.data.Add(new Datum() { ... });
temp.#params.Add(p);
Usually you'd change change the default constructors to initialize the lists already and prevent the list instances from being replaced by changing the properties to read-only, but that might not work well with JSON deserialization, so you really need to try this. It would look like this:
public class Param
{
public Param()
{
data = new List<Datum>();
}
public string url { get; set; }
public List<Datum> data { get; private set; }
}
public class RootObject
{
public RootObject()
{
#params = new List<Param>();
}
public List<Param> #params { get; private set; }
public string session { get; set; }
public int id { get; set; }
public string method { get; set; }
}

Categories

Resources