This question already has answers here:
Json.Net PopulateObject Appending list rather than setting value
(1 answer)
How to apply ObjectCreationHandling.Replace to selected properties when deserializing JSON?
(1 answer)
Closed 1 year ago.
I have this silly class:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
public List<int> NumbersList { get; set; }
}
And I have this two jsons (the second one coming out from a conditional serialization trough ShouldSerialize)
var json1 = "{\"Name\":\"Joe\",\"Surname\":\"Satriani\",\"Age\":40,\"NumbersList\":[10,20,30]}";
var json2= "{\"Name\":\"Mike\",\"NumbersList\":[40,50,60]}";
Also, I have a silly class to display the results:
private void showResult(object theClass)
{
var result = JsonConvert.SerializeObject(theClass);
Debug.WriteLine("result: " + result);
}
Now, I create a class with the first json:
var myPerson = JsonConvert.DeserializeObject<Person>(json1);
And everything is fine: the class gets all the values it should:
showResult(myPerson);
result: {"Name":"Joe","Surname":"Satriani","Age":40,"NumbersList":[10,20,30]}
Now, I want to apply the second json to the already existing class instance:
JsonConvert.PopulateObject(json2, myPerson);
But... this is what I get:
showResult(myPerson);
result: {"Name":"Mike","Surname":"Satriani","Age":40,"NumbersList":[10,20,30,40,50,60]}
So, as far as I understand, the PopulateObject correctly rewrites, as expected, the standard field properties (because I don't get "JoeMike" as Name, I get only "Mike" and this if fine), however, it appends list/indexed ones, because I don't obtain "40,50,60" but "10,20,30,40,50,60"...
So, my question is: is there a way to avoid PopulateObject to deliberately append List/Index/Enumerable objects ?
In the JsonSerializerSettings class, set the ObjectCreationHandling value to ObjectCreationHandling.Replace. With this setting, the information is no longer appended but copied.
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings()
{
ObjectCreationHandling = ObjectCreationHandling.Replace
};
JsonConvert.PopulateObject(json2, myPerson, jsonSerializerSettings);
ObjectCreationHandling setting
Related
This question already has answers here:
Detect if deserialized object is missing a field with the JsonConvert class in Json.NET
(5 answers)
Closed 5 years ago.
I have simple class:
public class TestObject {
public string Label { get; set; } = string.Empty;
public double Number { get; set; } = 0;
}
I would like to get an instance of TestObject from json. The trick is to allow Label and Number properties be optional, but throw exception if any extra field are added (to avoid mistake like "Lebel").
Code below alway convert to TestObject:
var correctInput = $"{{\"Label\":\"a\", \"Number\":5 }}";
var incorrectInput = $"{{\"Labell\":\"a\", \"Numberr\":5 }}";
JToken obj1 = JsonConvert.DeserializeObject<dynamic>(correctInput);
JToken obj2 = JsonConvert.DeserializeObject<dynamic>(incorrectInput);
var correctResult = obj1.ToObject<TestObject>();
var incorrectResult = obj2.ToObject<TestObject>();
Console.WriteLine($"Label: {correctResult.Label} Number: {correctResult.Number}");
Console.WriteLine($"Label: {incorrectResult.Label} Number: {incorrectResult.Number}");
The output of it will be:
Label: a Number: 5
Label: Number: 0
Only solution which comes to my mind is to define extra field with property [JsonExtensionData] and throw exception to set accessor:
[JsonExtensionData]
private IDictionary<string, JToken> _extraStuff {
get { return _extraStuff1; }
set { throw new Exception("cannot do it");}
}
But it is quite ugly hack.
I think you need to set MissingMemberHandling = MissingMemberHandling.Error
In case Label property in JSON is misspeled as 'Labell'
var incorrectInput = $"{{\"Labell\":\"a\", \"Numberr\":5 }}";
JsonConvert.DeserializeObject<TestObject>(
incorrectInput,
new JsonSerializerSettings() { MissingMemberHandling = MissingMemberHandling.Error });
that will force to throw JsonSerializationException:
'Could not find member 'Labell' on object of type 'TestObject'.
Refer to the Newtonsoft docs for more information # https://www.newtonsoft.com/json/help/html/DeserializeMissingMemberHandling.htm
This question already has answers here:
Can Newtonsoft Json.NET skip serializing empty lists?
(4 answers)
Closed 3 years ago.
In my POCO object, I have some sub objects that may or may not have some data in them. However, they're declared during object initialization so they're not null.
When I convert them to JSON objects, they show up even if I set the NullValueHandling to Ignore because they're not null.
What's the best way to deal with them so that they don't show up when I serialize my POCO objects to JSON?
Here's an example of a POCO object:
public class Person
{
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("firstName")]
public string FirstName { get; set; }
[JsonProperty("lastName")]
public string LastName { get; set; }
[JsonProperty("addresses", NullValueHandling = NullValueHandling.Ignore)]
public List<Address> Addresses { get; set; } = new List<Address>();
}
In this example, even if I don't have any addresses for this person, when serialized the person class, I see addresses: [] as an empty array.
I really would like to be able to ignore all properties with no data in them. What's the best approach to handle this?
Well the answers seems to be pretty simple :
Can Newtonsoft Json.NET skip serializing empty lists?
If you are permitted to extend the original class then add a
ShouldSerializePropertyName function to it. This should return a
Boolean indicating whether or not that property should be serialized
for the current instance of the class. In your example this might look
like this (not tested but you should get the picture):
This question already has answers here:
Keep casing when serializing dictionaries
(4 answers)
Closed 7 years ago.
I'm using json.net (Newtonsoft's JsonSerializer). I need to customize serialization in order to meet following requirements:
property names must start with lower case letter.
Dictionary must be serialized into jsonp where keys will be used for property names. LowerCase rule does not apply for dictionary keys.
for example:
var product = new Product();
procuct.Name = "Product1";
product.Items = new Dictionary<string, Item>();
product.Items.Add("Item1", new Item { Description="Lorem Ipsum" });
must serialize into:
{
name: "Product1",
items : {
"Item1": {
description : "Lorem Ipsum"
}
}
}
notice that property Name serializes into "name", but key Item1 serializes into "Item1";
I have tried to create CustomJsonWriter to serialize property names, but it changes also dicionary keys.
public class CustomJsonWriter : JsonTextWriter
{
public CustomJsonWriter(TextWriter writer) : base(writer)
{
}
public override void WritePropertyName(string name, bool escape)
{
if (name != "$type")
{
name = name.ToCamelCase();
}
base.WritePropertyName(name, escape);
}
}
You could try using the CamelCasePropertyNamesContractResolver.
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
var json = JsonConvert.SerializeObject(product, serializerSettings);
I'm just not sure how it'll handle the dictionary keys and I don't have time right this second to try it. If it doesn't handle the keys correctly it's still worth keeping in mind for the future rather than writing your own custom JSON writer.
You can use a JsonProperty to change how something is serialized/deserialized. When you define your object add the property items to the fields you would like represented differently in the JSON. This only works with NewtonsoftJSON. Other libraries may do it differently.
public class Product
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("items")]
public Dictionary<string, Item> Items { get; set; }
}
public class Item
{
[JsonProperty("description")]
public string Description { get; set; }
}
I am connecting you Google Places API to retrive results in the form of a JSON string. You can view the complete format of the string Here.
If you a look at it you will see that the actual results array starts after two elements which are html_attributions and next_page_token.
So When i try to deserialize it in this way:
var serializer = new JavaScriptSerializer();
var arr= serializer.Deserialize(result,typeof(string[]));
I get an empty array.
My question is how is there a way i can separate html_attributions and next_page_token fields and the pass the valid results array from the string to be deserialized?
I don't understand the part where you wish to seperate the html_attributions and the next_page_token.
Wouldn't it be sufficient to just deserialize the response with whatever properties that you need?
For example, you can deserialize the response to only retrieve the values that you desire;
// I represent the wrapper result
class Result
{
public List<string> html_attributions { get; set; }
public string next_page_token { get; set; }
public List<ResultItem> results { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
}
// the actual deserialization
Result Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize(json, typeof(Result));
}
Edit:
The reason that your deserialization doesn't return you a array of strings is because the response that you retrieve is infact an object and not an array, however the parameter within that object which is named results is an array. In order for you to deserialize more properties you'll have to define them in your "ResultItem" class, sorry for my poor naming here.
For instance, if you'd wish to also retrieve the icon property per result you'll have to add a property named "icon" of type string.
Meanwhile the property "photos" is an array, in order to deserialize it you'll have to create another class and add a property of type list/array of that newly created class, and it has to be named "photos" unless you use a different serializer or use DataContract and DataMember attributes (using the Name property for field mapping).
// the representation of a photo within a result item
class Photo
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
// the added icon
public string icon { get; set; }
// the added photos collection, could also be an array
public List<Photo> photos { get; set; }
}
Just look at the JSON result to figure out what other properties that you might want to add, for instance the "scope" property is an string whilst the "price_level" is an integer.
If I understand your comment correctly you're only interested in the actual results, you'll still have to deserialize the response correctly with its wrapper.
// the actual deserialization
List<ResultItem> Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize(json, typeof(Result));
return result.results;
}
Edit2:
If you really want a string[] as a result you could simply take use of System.Linq using the code above.
string[] stringArray = result.results.Select(r => string.Format("id:{0} - name:{1}", r.id, r.name)).ToArray();
Edit3:
Instead of using the JavascriptSerializer you could use JObject functionality which can be found in the Newtonsoft.Json.Linq library.
var jsonObject = JObject.Parse(json);
string[] results = jsonObject.SelectTokens("results").Select(r => r.ToString()).ToArray();
This will give you an array of strings where each value within the array is the actual json string for each result.
If you however would like to query for the coordinates only:
var jsonObject = JObject.Parse(json);
var coordinates = jsonObject["results"]
.Select(x => x.SelectToken("geometry").SelectToken("location"))
.Select(x => string.Format("{0},{1}", (string)x.SelectToken("lat"), (string)x.SelectToken("lng")))
.ToArray();
This would give you an array of coordinates, eg:
[
"-33.867217,151.195939",
"-33.866786,151.195633",
...
]
Whatever approach you choose you'll be able to accomplish same results using either Newtonsoft or the .net serializer, whilst the Newtonsoft approach would allow you to query without creating strong types for deserialization.
I don't find the point of "[...] pass the valid results array from the string to be deserialized".
Maybe you need to switch to JSON.NET and do something like this:
// You simply deserialize the entire response to an ExpandoObject
// so you don't need a concrete type to deserialize the whole response...
dynamic responseEntity = JsonConvert.DeserializeObject<ExpandoObject>(
googlePlacesJson, new ExpandoObjectConverter()
);
// Now you can access result array as an `IEnumerable<dynamic>`...
IEnumerable<dynamic> results = responseEntity.results;
foreach(dynamic result in results)
{
// Do stuff for each result in the whole response...
}
This question already has answers here:
Adding unknown (at design time) properties to an ExpandoObject
(5 answers)
Closed 8 years ago.
I'm trying to create a some Json in my MVC app and I only want to include the properties from my source object, if it has some properties values, set.
eg.
public class Foo
{
public string Aaaa { get; set; }
public string Bbbb { get; set; }
public int? Ccccc { get; set; }
public Lol Dddd { get; set; }
}
// Example Outputs.
Aaaa and Ccccc have values only:
return Json(new { Aaaa = source.Aaaa, Cccc = source.Ccccc.Value };
Dddd only has been set.
return Json(new { Dddd = source.Dddd }
See how i was trying to create an anonymous object on the fly. Well, I can do that because in this contrite example, I know what was set. But when it comes to real code, I would have to do 'figure out' what was really set and then dynamically return that.
The idea is based upon Stack Exchange's Api Wrapper .. where they have some optional values that they return via json, if they are set.
Take a look at the ExpandoObject, an example with xml is given here
eg.
dynamic contact = new ExpandoObject();
contact.Name = "Patrick Hines";
contact.Phone = "206-555-0144";
... etc ...