Newtonsoft.Json Deserialize Collection using "Add" - c#

Sorry for my English
When object has some "side effect" to change the property,
Result not same as before deserialize,
I know shouldn't serialize object has "side effect",
But i need walk around as this moment
Json:
{
"SerializeBar": "bar1,bar2",
"Bar": [
"bar1",
"bar2" <-- only has 2 item
]
}
Deserialize:
var deserialzedObject = JsonConvert.DeserializeObject<Model>(json);
deserialzedObject.Bar.ForEach(x => Console.WriteLine(x));
//bar1
//bar2
//bar1 <-- why JsonConvert using "Add", not replace WHOLE List
//bar2
Console.WriteLine(deserialzedObject.SerializeBar);
//bar1,bar2,bar1,bar2
Model:
class Model
{
public string SerializeBar
{
get { return string.Join(",", Bar); }
set { Bar = value.Split(',').ToList(); }
}
public List<string> Bar { get; set; }
}
PS: not accept answer attribute [JsonIgnore], or reorder Property position because real case is to complex

You have to set JsonSerializerSettings
JsonConvert.DeserializeObject<Model>(json, new JsonSerializerSettings()
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});

Related

Parse JSON Objects with unique strings as parents using JSON.Net

Let's say I have this example JSON:
"Test": {
"KIf42N7OJIke57Dj6dkh": {
"name": "test 1"
},
"xsQMe4WWMu19qdULspve": {
"name": "test 2"
}
}
I want to parse this into an Array of a custom class I have, which will be exampled below:
class Class1 {
public string Name { get; set; }
Class1(string name) {
Name = name;
}
}
How can I parse this using Json.NET's JObject.Parse?
You can achieve your goal with JPath query like this :
var myArray = JObject
.Parse(json)
.SelectTokens("$.Test..name")
.Values<string>()
.Select(s => new Class1(s))
.ToArray();
But probably not the best way to do it.
I personnaly prefere to create classes to represent the json structure and then apply transformations.
void Main()
{
var json = #"{""Test"": {
""KIf42N7OJIke57Dj6dkh"": {
""name"": ""test 1""
},
""xsQMe4WWMu19qdULspve"": {
""name"": ""test 2""
}
}
}";
var root = JsonConvert.DeserializeObject<Root>(json);
var array = root.Test.Select(i => i.Value).ToArray();
array.Dump();
}
public class Root
{
public Dictionary<string, Class1> Test { get; set; }
}
public class Class1
{
public string Name { get; set; }
public Class1(string name)
{
Name = name;
}
}
To begin with, your Json is missing starting/closing braces. The Json needs to have wrapping braces around the Test value.
{
'Test':
{
'KIf42N7OJIke57Dj6dkh': {'name': 'test 1'},
'xsQMe4WWMu19qdULspve': {'name': 'test 2'}
}
}
If you are missing it in the original Json, you could wrap the current input Json as following.
var correctedJson = $"{{{inputJsonString}}}";
If you want to parse the Json Objects to Array of Class1 without creating additional concrete data structures and using JPath Queries, you could use Anonymous Types for the purpose using the DeserializeAnonymousType Method proved by Json.Net. For example,
var sampleObject = new {Test = new Dictionary<string,Class1>()};
var data = JsonConvert.DeserializeAnonymousType(correctedJson,sampleObject);
var result = data.Test.Select(x=>x.Value).ToArray();
You could also achieve it using JPath Query or creating Concrete Data Structures as #Kalten as described in his answer.

How to use JSON to fill ObservableCollection?

How to fill ObservableCollection using JSON? Now there is only the script itself and the model in the desktop application. I can not understand how to tie it up.
I get it after running the script:
{
"records": [
{
"brand_id": "1",
"brand_name": "Gigabyte"
},
{
"brand_id": "2",
"brand_name": "MSI"
},
{
"brand_id": "3",
"brand_name": "Lenovo"
},
{
"brand_id": "4",
"brand_name": "Dell"
},
{
"brand_id": "5",
"brand_name": "Google"
}
]}
And I have a Model in app:
public class Brands
{
int brand_id;
string brand_name;
public int Brand_id { get => brand_id; set => brand_id = value; }
public string Brand_name { get => brand_name; set => brand_name = value; }
}
And Collection:
public class BrandsCollection
{
private ObservableCollection<Brands> brands;
public ObservableCollection<Brands> Brands { get => brands; set => brands = value; }
}
It is fairly straight forward, especially with packages available to simplify a lot of the work. The Nuget Package System.Net.Http will have the packages you need to create an HttpClient to Get() your JSON from the web. I recommend Newtonsoft.Json to parse JSON into a C# object. And then having the object you just need to set the DataGrid.ItemSource to be an array of objects of any type for it to generate columns.
A simple example:
First you would define a simple object representation of your JSON data.
For instance if you had the following data:
[
{
"Name":"Test",
"Data": ["Item1","Item2"]
},
{
"Name":"Test 2",
"Data": ["Item3","Item4"]
}
]
You would have to create an equivalent C# representation.
Basically this is a List of objects so:
public class OuterObject : List<InnerObject> {}
The inner object is as follows:
public class InnerObject {
public string Name { get; set; }
public List<string> Data { get; set; }
}
Having the objects defined you can do something like:
HttpClient client = new HttpClient { BaseAddress = new Uri("ADDRESS") };
var json = await client.GetAsync("/ENDPOINT");
JsonSerializer serializer = JsonSerializer.CreateDefault();
using (StringReader reader = new StringReader(json))
{
using (JsonTextReader jsonReader = new JsonTextReader(reader))
{
var result = serializer.Deserialize<OuterObject>(jsonReader);
}
}
Then to access the data in your program you can access it like so:
string name = result[0].Name;
Or set it to a DataGrid's ItemSource to have the Data show up magically.
grid1.ItemSource = result;
As long as the result is an array of items it will create a row per item.
You may want to specify which items are shown but that is done by modifying DataGrid.Columns definitions and setting DataGrid.AutogenerateColumns = false
EDIT: With your data and models
//Just a small change to the Collection
public class BrandsCollection {
private ObservableCollection<Brands> _records;
public ObservableCollection<Brands> records { get => _records; set => _records= value; }
}
And to parse the data...
JsonSerializer serializer = JsonSerializer.CreateDefault();
using (StringReader reader = new StringReader(json))
{
using (JsonTextReader jsonReader = new JsonTextReader(reader))
{
var result = serializer.Deserialize<BrandsCollection>(jsonReader);
}
}
You have to remember to either use the same names as the json labels or use Json Attributes. For more info on that you can go to the official Newtonsoft.Json documentation

Adding a root element to json

I am generating a json string using NewtosoftJson using a table to format the the json. This is a simple key value pairs list and looks like:
public class items
{
private string key = String.Empty;
private string value = String.Empty;
public string Key
{
get
{
return key;
}
set
{
if (value != key)
{
key = value;
}
}
}
public string Value
{
get
{
return value;
}
set
{
if (value != this.value)
{
this.value = value;
}
}
}
}
When a list is populated and then serialised I get this JSON:
"Items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
What I need to get is:
"customData": {
"items": [
{
"Key":"FirstValue",
"Value":"One"
},
{
"Key":"SecondValue",
"Value":"Two"
},
{
"Key":"ThirdValue",
"Value":"Three"
}
]
}
I have tried creating a second class CustomData but can't see how to get the original JSON into the second class! Could you advice me on the correct way to construct the second class and method used to populate it please.
You can create an anonymous object and serialize that:
var objContainingItems = ... // your usual code
var customDataObj = new { customData = objContainingItems };
string json = JsonConvert.SerializeObject(customDataObj);
This is the most convenient solution if all you are interested in is serializing.
If you also want to be able to deserialize it, then you will need to use a class as specified in the answer by #William Moore.
Create a class customData and create a reference to the class items inside it. Then serialise your customData class using Newtonsoft.Json rather than your items class. So you will have:
public class CustomData
{
public items[] items; // I would rename the class items to item
}
Then you have an object of type customData, called customData which you pass into Newtonsoft.
You could then use the following to serialise/deserialise your data:
CustomData input = new CustomData();
input.items = []; // Whatever you have at the moment?
string json = JsonConvert.SerializeObject(account) //optionally set Formatting.Indented
CustomData deserialised = JsonConvert.DeserializeObject<CustomData>(json);

How to serialize-deserialize any of 3 object types, one of which contains an abstract class?

I am sending 3 .net objects over the network:
- List<int>
- List<ParentObject>
- string
This is how I'm serializing (same for all types):
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Arrays
};
string message = JsonConvert.SerializeObject(listOfParents, JSsettings);
//listOfParents is of type List<ParentObject>
The ParentObject is an abstract class and has two child classes. It has a property to get which child type it represents.
public enum EntityType {Child1, Child2};
class ParentObject
{
public EntityType et { get; set; }
//..other members
}
I want to call 3 different functions based on which of the 3 objects is received.
Object genericObject = JsonConvert.DeserializeObject(message, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
});
if (genericObject is List<int>)
{
List<int> myList= (List<int>)genericObject;
myfunction1(myList);
}
if (genericObject is List<ParentObject>)
{
//etc..
The ParentObject is causing problems at DeserializeObject() because it says "Could not create an instance of type ParentObject. Type is an interface or abstract class and cannot be instantiated". So I was thinking I might need to use CustomCreationConverter at http://james.newtonking.com/projects/json/help/index.html?topic=html/CustomCreationConverter.htm
That still wouldn't solve my problem since the CustomCreationConverter needs the type during deserialization whereas I don't check the type until after deserialization.
Any suggestions to solve the problem?
If I use objects defined like this:
public enum EntityType { Child1, Child2 };
abstract class ParentObject
{
public EntityType et { get; set; }
}
class ChildClass : ParentObject
{
public int ChildClassProp { get; set; }
public ChildClass()
{
this.et = EntityType.Child1;
}
}
class ChildClass2 : ParentObject
{
public int ChildClass2Prop { get; set; }
public ChildClass2()
{
this.et = EntityType.Child2;
}
}
Then I can happily deserialize derived classes(ChildClass and ChildClass2) to ParentObject like this:
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects
};
List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });
string message = JsonConvert.SerializeObject(list,
Newtonsoft.Json.Formatting.Indented, JSsettings);
list = JsonConvert.DeserializeObject<List<ParentObject>>(message, JSsettings);
Where message looks like this:
[
{
"$type": "ConsoleApplication4.ChildClass, ConsoleApplication4",
"ChildClassProp": 1,
"et": 0
},
{
"$type": "ConsoleApplication4.ChildClass2, ConsoleApplication4",
"ChildClass2Prop": 2,
"et": 1
}
]
The key in this one was using TypeNameHandling = TypeNameHandling.Auto for both serialization and deserialization. Using TypeNameHandling.Arrays creates a message that looks like this:
{
"$type": "System.Collections.Generic.List`1[[ConsoleApplication4.ParentObject, ConsoleApplication4]], mscorlib",
"$values": [
{
"ChildClassProp": 1,
"et": 0
},
{
"ChildClass2Prop": 2,
"et": 1
}
]
}
Note that the types of the list items are not included, only the type of the list, hence the error you were getting.
Edit:
I think the easiest way to get this working the way you want is to define a simple class like this one that acts as a thin wrapper around the object you are serializing:
class ObjectContainer
{
public object Data { get; set; }
}
Then the code would look like this (note the change to TypeNameHandling.Auto):
JsonSerializerSettings JSsettings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto
};
List<ParentObject> list = new List<ParentObject>();
list.Add(new ChildClass() { ChildClassProp = 1 });
list.Add(new ChildClass2() { ChildClass2Prop = 2 });
ObjectContainer container = new ObjectContainer()
{
Data = list
};
string message = JsonConvert.SerializeObject(container,
Newtonsoft.Json.Formatting.Indented, JSsettings);
var objectContainer = JsonConvert.DeserializeObject<ObjectContainer>(message, JSsettings);
if (objectContainer.Data is List<int>)
{
Console.Write("objectContainer.Data is List<int>");
}
else if (objectContainer.Data is List<ParentObject>)
{
Console.Write("objectContainer.Data is List<ParentObject>");
}
else if (objectContainer.Data is string)
{
Console.Write("objectContainer.Data is string");
}
The reason I have gone for this approach is that Json.Net will take care of almost all the work. Simply calling the non-generic JsonConvert.DeserializeObject method is fine, but you would then need to do additional work as this method returns a JContainer, not an object.

Serialize dictionary as array (of key value pairs)

Json.Net typically serializes a Dictionary<k,v> into a collection;
"MyDict": {
"Apples": {
"Taste": 1341181398,
"Title": "Granny Smith",
},
"Oranges": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
},
}
Which is great. And from looking around on SO it seems to be what most people want. However, in this particular case, I want to serialize between my Dictionary<k,v> and the Array format instead;
"MyDict": [
"k": "Apples",
"v": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
"k:": "Oranges",
"v:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
Is there an easy way to do this with my existing field type? Is there an attribute I can annotate for instance?
Another way to accomplish this is to use a custom ContractResolver. That way you do not have to subclass Dictionary<K,V> nor apply a transform each time you serialize, as suggested in other answers.
The following resolver will cause ALL dictionaries to be serialized as an array of objects with "Key" and "Value" properties:
class DictionaryAsArrayResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (objectType.GetInterfaces().Any(i => i == typeof(IDictionary) ||
(i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))))
{
return base.CreateArrayContract(objectType);
}
return base.CreateContract(objectType);
}
}
To use the resolver, add it to your JsonSerializerSettings, then pass the settings to JsonConvert.SerializeObject() like this:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.ContractResolver = new DictionaryAsArrayResolver();
string json = JsonConvert.SerializeObject(obj, settings);
Here is a working demo.
Ah, it turns out this is as straightforward as I'd hoped. My Dictionary<k,v> is subclassed already and I found that I can annotate it with [JsonArrayAttribute]. That gives me exactly the format I need;
"MyDict": [
{
"Key": "Apples",
"Value": {
"Taste": 1341181398,
"Title": "Granny Smith",
}
},
{
"Key:": "Oranges",
"Value:": {
"Taste": 9999999999,
"Title": "Coxes Pippin",
}
},
]
For this example, I'll use the dictonary:
var myDict = new Dictionary<string,string>() {
{"a","123"},
{"b","234"},
{"c","345"}
};
which serializes (with Newtonsoft.Json.JsonConvert.SerializeObject(myDict)) to:
{"a":"123","b":"234","c":"345"}
You could do a transform using LINQ to create an anonymous object, and serialize that:
var myDictTransformed = from key in myDict.Keys
select new { k = key, v = myDict[key] };
Or you could use a real object
class MyDictEntry
{
public string k { get; set; }
public string v { get; set; }
}
and either the above or the alternative LINQ syntax:
var myDictTransformed = myDict.Keys.AsEnumerable()
.Select(key => new MyDictEntry{
k = key,
v = myDict[key]
});
Either way, this serializes to:
[
{"k":"a", "v":"123"},
{"k":"b", "v":"234"},
{"k":"c", "v":"345"}
]
.NET Fiddle link: https://dotnetfiddle.net/LhisVW
The simplest solution I found is to convert your Dictionary<string, string> to a List<KeyValuePair<string, string>>. JSON.NET then converts your List into an array of objects with the form { Key: 'keyname', Value: 'value' }. This works well if you accept the required model change and don't want to subclass your Dictionary.
gregmac's answer was helpful, but didn't quite work. The following is the same idea... without the puns.
var dictionaryTransformed = dictionary.Select(item => item.Key).Select(i =>
new {Key = i, Value = dictionary[i] });
or of course
var dictionaryTransformed = dictionary.Select(item =>
new {item.Key, Value = dictionary[item.Key] });
Then to json
var json = (new JavaScriptSerializer()).Serialize(
new { Container = dictionaryTransformed.ToArray() } )
I'm not exactly sure why, but the custom ContractResolver by Brian Rogers listed above didn't work for me. It seemed to get into an endless loop somewhere internally. Possibly due to other parts of my json.net setup.
Anyway - this workaround did the trick for me.
public interface IStrongIdentifier
{
string StringValue { get; set; }
}
public class StrongIdentifierKeyedDictionaryWrapper<TKey, TValue> : Dictionary<string, TValue>
where TKey : IStrongIdentifier
{
public void Add(TKey key, TValue value)
{
base.Add(key.StringValue, value);
}
public void Remove(TKey key)
{
base.Remove(key.StringValue);
}
public TValue this[TKey index]
{
get => base[index.StringValue];
set => base[index.StringValue] = value;
}
public bool ContainsKey(TKey key)
{
return base.ContainsKey(key.StringValue);
}
}

Categories

Resources