Newtonsoft.JSON appends Lists when deserializing with FromObject - c#

I've simplified my problem down to this bit of sample code using Newtonsoft.JSON.Linq and JObject.ToObject<T>()
The issue here is that in the constructor of Test a default value is added to the list Nums. However if I serialize and then deserialize the object, Newtonsoft is not replacing the list, but appending to the values set by the constructor.
I cannot change the deserialization process, as that is in a core library.
How can I set a default value for my list without duplication? Thanks.
class Test
{
public List<int> Nums { get; set; } = new List<int>();
public Test()
{
// add default value
this.Nums.Add(0);
}
}
class Program
{
static void Main(string[] args)
{
Test job = new Test(); // job.Nums.Count == 1
JObject jJob = JObject.FromObject(job);
job = jJob.ToObject<Test>(); // job.Nums.Count == 2
}
}
.Net Fiddle

Create ctor overload which takes default value.
class Test
{
public List<int> Nums { get; set; }
public Test()
{
this.Nums = new List<int>();
}
public Test(int def) : this()
{
this.Nums.Add(def);
}
}
Here is usage:
public class Program
{
public static void Main(string[] args)
{
Test job = new Test(0);
Console.WriteLine(job.Nums.Count);
JObject jJob = JObject.FromObject(job);
job = jJob.ToObject<Test>();
Console.WriteLine(job.Nums.Count); // This is 1 again.
}
}

You need to tell it how to deserialize the object
var serializer = new JsonSerializer {
ObjectCreationHandling = ObjectCreationHandling.Replace
};
job = jJob.ToObject<Test>(serializer);
Properly gives you 1 item after deserializing.

Related

Serialize custom type to JSON

class Program
{
static void Main(string[] args)
{
JSONClass jsonClass = new JSONClass();
JSONElement el = new JSONElement
{
A = 5,
B = "test1"
};
JSONElement el2 = new JSONElement
{
A = 3,
B = "test2"
};
jsonClass.JSONList.Add(el);
jsonClass.JSONList.Add(el2);
var output = JsonSerializer.Serialize<JSONClass>(jsonClass);
Console.WriteLine(output);
}
}
public class JSONClass
{
public List<JSONElement> JSONList = new List<JSONElement>();
}
public class JSONElement
{
public int A { get; set; }
public string B { get; set; }
}
This code returns {} which means that JsonSerializer.Serialize failed to do what it supposed to do. I imagine its because its not smart enough to handle custom types. And here is my question, how to do it. Internet is full of articles how to write custom converters etc, but none of them mention custom types.
Your JSONList member is a public field - whereas JsonSerializer looks for properties.
Change your code for JSONClass to this:
public class JSONClass
{
public List<JSONElement> JSONList { get; } = new List<JSONElement>();
}
The output is then:
{"JSONList":[{"A":5,"B":"test1"},{"A":3,"B":"test2"}]}
The bigger lesson to learn here is not to assume that the mistake is in the library you're using. Always start with an expectation that the problem is in your own code. Sometimes you'll find it really is in the library or system code (or in the compiler etc) but in my experience that's relatively rare.

How to add to a read-only 'List' property in class defined using expression body?

When I run the code below the 'Works' property on TestHelper returns 4 Test items as it should but the 'Fails' property only returns 1 Test item.
Sample Classes:
public class Test {
public string Name { get; set; }
}
public class TestHelper {
public TestHelper() {
AddTest(new Test() { Name = "1" });
}
public List<Test> Works { get; } = new List<Test>() {
new Test(){ Name="0"}
};
public List<Test> Fails => new List<Test>() {
new Test(){ Name="0"}
};
public TestHelper AddTest(Test test) {
Works.Add(test);
Fails.Add(test);
return this;
}
}
Sample Program:
var th = new TestHelper();
th.AddTest(new Test { Name = "2" });
th.Works.Add(new Test { Name = "3" });
th.Fails.Add(new Test { Name = "4" });
var json = Newtonsoft.Json.JsonConvert.SerializeObject(th);
Json Results:
{
"Works":[{"Name":"0"},{"Name":"1"},{"Name":"2"},{"Name":"3"}],
"Fails":[{"Name":"0"}]
}
Both the 'Works' & 'Fails' look to be defined as read-only List properties of Test.
Why does one definition method work and not the other?
Works is a read-only auto-implemented property with a default value, equivalent to this:
private List<Test> _works = new List<Test>() { new Test() { Name="0" } };
public List<Test> Works { get { return _works; } }
The value of Works is initialized once, when it is created. You are subsequently making modifications to the list by adding values - note that the property is read-only, not the List that it contains.
By contrast, Fails is a property whose getter returns a newly-created list every time you get it. It is equivalent to this:
public List<Test> Fails
{
get
{
return new List<Test>() { new Test() { Name = "0" } };
}
}

Deserializing Json array with variable names first using C# Json.NET

I'm getting an irregular JSON array from the Census Bureau's public api.
The variable names are all in the first element, and I'm having trouble deserializing it.
http://api.census.gov/data/2014/pep/agesex?get=AGE,POP,SEX&for=us:*&DATE=7
gives me JSON like this:
[["AGE","POP","SEX","DATE","us"],
["0","3948350","0","7","1"],
["1","3962123","0","7","1"],
["2","3957772","0","7","1"],
["3","4005190","0","7","1"],
["4","4003448","0","7","1"],
["5","4004858","0","7","1"],
["6","4134352","0","7","1"],
["7","4154000","0","7","1"]]
I can successfully deserialize this using:
var test1 = JsonConvert.DeserializeObject<String[][]>(jsonStr);
However, I'm trying to deserialize it to a class like this:
public class TestClass
{
public string AGE { get; set; }
public string POP { get; set; }
public string SEX { get; set; }
public string DATE { get; set; }
public string us { get; set; }
}
I'm trying to do this:
var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr);
But I'm getting the following exception:
An exception of type 'Newtonsoft.Json.JsonSerializationException'
occurred in Newtonsoft.Json.dll but was not handled in user code
Additional information: Cannot create and populate list type
TestClass. Path '[0]', line 1, position
2.
There's two parts to this.
First is turning the JSON in to data usable in C#, and the second is turning that data in to nice objects.
Here's a working dotNetFiddle.net example of the following code: https://dotnetfiddle.net/Cr0aRL
Each row in your JSON is made up of an array of strings.
So that's an array of an array of strings.
In C# that can be written as string[][].
So to turn the JSON in to usable data with JSON.Net you can do:
var json = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"],[\"0\",\"3948350\",\"0\",\"7\",\"1\"],[\"1\",\"3962123\",\"0\",\"7\",\"1\"],[\"2\",\"3957772\",\"0\",\"7\",\"1\"],[\"3\",\"4005190\",\"0\",\"7\",\"1\"],[\"4\",\"4003448\",\"0\",\"7\",\"1\"],[\"5\",\"4004858\",\"0\",\"7\",\"1\"],[\"6\",\"4134352\",\"0\",\"7\",\"1\"],[\"7\",\"4154000\",\"0\",\"7\",\"1\"]]";
var rawData = JsonConvert.DeserializeObject<string[][]>(json);
Next up is is turning that data in to objects.
The first row is the header, containing the column names, so we want to grab that, and then figure out the column index for each column name.
var headerRow = rawData.First();
var ageIndex = Array.IndexOf(headerRow, "AGE");
var popIndex = Array.IndexOf(headerRow, "POP");
var sexIndex = Array.IndexOf(headerRow, "SEX");
var dateIndex = Array.IndexOf(headerRow, "DATE");
var usIndex = Array.IndexOf(headerRow, "us");
Now we have the indexes, we need to take each row, and convert it in to the appropriate object. I've used LINQ for this as it's very good at representing data processing in a clear way.
var testData = rawData
.Skip(1) //The first row is a header, not data
.Select(dataRow => new TestClass()
{
AGE = dataRow[ageIndex],
POP = dataRow[popIndex],
SEX = dataRow[sexIndex],
DATE = dataRow[dateIndex],
us = dataRow[usIndex]
});
Finally a bit of testing, to make sure you have the data you're expecting.
//Get the second data row as an example
var example = testData.Skip(1).First();
//Output example POP to check value
Console.WriteLine(example.POP);
Everything above is very manual.
You have to know what headers you expect, then you manually find the indexes, then you manually map the rows to objects.
It's quite possible for a simple use case that doing that is fine. But in larger and/or more complex systems you might want/need to automate those steps.
Automating those steps is possible, but is beyond the scope of this answer as how you approach it can depend on a lot of different factors.
You could make a custom JsonConverter to handle this conversion during deserialization. The conversion code is really not much different than other answers here, except that it is encapsulated into a separate class so that you don't muddy up your main code with the conversion details. From the point of view of your main code it "just works".
Here is how to write the converter:
public class TestClassArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(TestClass[]));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray table = JArray.Load(reader);
TestClass[] items = new TestClass[table.Count - 1];
for (int i = 1; i < table.Count; i++)
{
JArray row = (JArray)table[i];
items[i - 1] = new TestClass
{
AGE = (string)row[0],
POP = (string)row[1],
SEX = (string)row[2],
DATE = (string)row[3],
us = (string)row[4]
};
}
return items;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And here is how you would use it:
var test2 = JsonConvert.DeserializeObject<TestClass[]>(jsonStr, new TestClassArrayConverter());
Fiddle: https://dotnetfiddle.net/68Q0KT
You have to do the processing on your own, as there is no way the json deserializer can know, how to put the values into the respecitve variables.
If you know, this will be exactly this structure, you could for instance add an appropriate constructor
public TestClass(string[] values) {
AGE = values[0];
...
}
to your class. Then serialize your result to array of arrays of string and then pass the inner arrays to your constructor.
var t1 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
//skip the first entry, as this contains the headers
var t2 = t1.Skip(1).Select(x=> new TestClass(x));
If your structure varies, you'll have to write some more complicated mapping code.
You will have to do some custom mapping as your Json does not have any naming conventions so you will have to work with the data in array and index formats. This will work:
var jsonStr = "[[\"AGE\",\"POP\",\"SEX\",\"DATE\",\"us\"], [\"0\",\"3948350\",\"0\",\"7\",\"1\"], [\"1\",\"3962123\",\"0\",\"7\",\"1\"], [\"2\",\"3957772\",\"0\",\"7\",\"1\"], [\"3\",\"4005190\",\"0\",\"7\",\"1\"], [\"4\",\"4003448\",\"0\",\"7\",\"1\"], [\"5\",\"4004858\",\"0\",\"7\",\"1\"], [\"6\",\"4134352\",\"0\",\"7\",\"1\"], [\"7\",\"4154000\",\"0\",\"7\",\"1\"]]";
var test2 = JsonConvert.DeserializeObject<string[][]>(jsonStr);
var test3 = test2.Select(x => new TestClass()
{
AGE = x[0].ToString(),
POP = x[1].ToString(),
SEX = x[2].ToString(),
DATE = x[3].ToString(),
us = x[4].ToString()
}).ToList();
//test Case
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
namespace ApiController.Test
{
[TestClass]
public class DownloadIrregularJsonStringObjects
{
string ApiKey => "YourPersonalCensusKey";
/// <summary>
/// You have to get your own ApiKey from the Census Website
/// </summary>
[TestMethod]
public void TestGetItem()
{
string url = $"http://api.census.gov/data/timeseries/healthins/sahie?get=NIC_PT,NAME,NUI_PT&for=county:*&in=state:*&time=2015&key={YourPersonalCensusKey}";
string expected = "Autauga County, AL";
IList<HealthData> actual = ApiController.DownloadIrregularJsonStringObjects.GetCensusHealthData(url);
Assert.AreEqual(actual[0].NAME, expected);
}
}
}
///Actual Assembly
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace ApiController
{
public class DownloadIrregularJsonStringObjects
{
public static IList<HealthData> GetCensusHealthData(string url)
{
var json = GetData(url);
var rawData = JsonConvert.DeserializeObject<string[][]>(json);
var headerRow = rawData.First();
var nic_pt_Index = Array.IndexOf(headerRow, "NIC_PT");
var name_Index = Array.IndexOf(headerRow, "NAME");
var nui_pt_Index = Array.IndexOf(headerRow, "NUI_PT");
IList<HealthData> retVal = new List<HealthData>();
foreach (var r in rawData.Skip(1))
{
HealthData dataRow = new HealthData();
dataRow.NIC_PT = r[nic_pt_Index];
dataRow.NAME = r[name_Index];
dataRow.NUI_PT = r[nui_pt_Index];
retVal.Add(dataRow);
}
return retVal;
}
private static string GetData(string url)
{
using (var w = new WebClient())
{
var jsonData = string.Empty;
jsonData = w.DownloadString(url);
return jsonData;
}
}
}
public class HealthData
{
public string NIC_PT { get; set; }
public string NAME { get; set; }
public string NUI_PT { get; set; }
}
}

XML Serialization with optional segment

I have a problem when i want to serialize below to XML because segment E1WPU03 is optional but when it has a value then I need to put it under segment E1WPU02
I've been try for two days but it end up it writes segmen E1WPU02 first then segment E1WPU03 which doesn't work
How can I do this?
XmlSerializer doesn't normally interleave elements. You can trick it, however, with a List<> and multiple [XmlElement(...)] specifying a type:
class Program
{
static void Main()
{
var root = new MyXmlRoot
{
Items = {
new Bar { },
new Blap { },
new Bar { },
}
};
var ser = new XmlSerializer(typeof(MyXmlRoot));
ser.Serialize(Console.Out, root);
}
}
public abstract class Foo { } // base type used for the list
public class Bar : Foo {
// more props here
}
public class Blap : Foo {
// more props here
}
public class MyXmlRoot
{
private readonly List<Foo> items = new List<Foo>();
[XmlElement("E1WPUO2", typeof(Bar))]
[XmlElement("E1WPUO3", typeof(Blap))]
public List<Foo> Items { get { return items; } }
}
Other elements like E1WPU01 do not need to go in Items if there is no need to interleave them - they can go directly on MyXmlRoot (as you probably already have).

How to read the properties of an object inside an arraylist

I'm having some issues reading the properties of an item I have placed into an arraylist and I can't find the answer anywhere.
ArrayList itemsArrayList = new ArrayList();
itemsArrayList.Add(abyssalScepter);
itemsArrayList.Add(aegisOfTheLegion);
itemInBuildAbilityPower = itemsArrayList[0].abilityPower;
I need to be able to read the properties of the objects in the array so I can apply their values elsewhere but this gets me nowhere.
You need to cast object to the expected type (and hope it's really of this type).
itemInBuildAbilityPower = ((Item)itemsArrayList[0]).abilityPower;
The better option (if the infrastructure code is yours) to use generic container, e.g. List<T>.
List<Item> itemsArrayList = new List<Item>
itemsArrayList.Add(abyssalScepter);
itemsArrayList.Add(aegisOfTheLegion);
itemInBuildAbilityPower = itemsArrayList[0].abilityPower;
try
var itemInBuildAbilityPower = itemsArrayList[0].GetType().GetProperty ("abilityPower").GetGetMethod().Invoke (itemsArrayList[0], null);
Building on elder_george's answer, here is an example of what you could do if abyssalScepter and aegisOfTheLegion are not the exact same type:
using System.Collections.Generic;
class Power { }
interface IAbilityPower { Power abilityPower { get; set; } }
class Scepter : IAbilityPower { public Power abilityPower { get; set; } }
class Aegis : IAbilityPower { public Power abilityPower { get; set; } }
class Test
{
public static void Main()
{
var abyssalScepter = new Scepter();
var aegisOfTheLegion = new Aegis();
var itemsList = new List<IAbilityPower>();
itemsList.Add(abyssalScepter);
itemsList.Add(aegisOfTheLegion);
var power = itemsList[0].abilityPower;
}
}

Categories