I have such a class:
public class item
{
public string Name { get; set; }
public string City { get; set; }
public string Pw { get; set; }
}
from which I create several objects I store in the DB. Then I want to update one of them with data coming from client in the form of a json like this:
{
"Name":"John",
"City":"NYC"
}
the idea would be to use:
item myitem = JsonConvert.DeserializeObject<item>(jsoncomingfromclient);
but doing so Pw is overwritten with null (while obviously I want to keep the original value)
NullValueHandling looks like a good candidate but it works if the value is null, in my case it is completely missing from the json.
Any idea how to deserialize a json keeping the old value in the destination object if the value is missing in the json?
Use JsonConvert.PopulateObject. It's designed for this purpose:
var item = new item { Name = "my name", City = "my city", Pw = "my pw" };
var json = #"
{
""Name"":""John"",
""City"":""NYC""
}";
JsonConvert.PopulateObject(json, item);
Debug.Assert(item.Pw == "my pw"); // no assert
Debug.Assert(item.Name == "John"); // no assert
Debug.Assert(item.City == "NYC"); // no assert
This part of code JsonConvert.DeserializeObject<item>(jsoncomingfromclient); will create new instance of type item based on parameter jsoncomingfromclient and return it.
This part item myitem = ... declares a variable myitem of type item and gives it a concrete instance. So there is no way to merge anything like this.
You just have to write some merge method manually and define what and how is merged between two objects.
Something like that
item dbitem = ...
item myitem = JsonConvert.DeserializeObject<item>(jsoncomingfromclient);
item mergedItem = myitem.merge(dbitem)
Related
In C#, I want to replace the string Placeholder with Object Properties using Reflection
string formula = "{\"Name\": \"{{Name}}\", \"Email\": \"{{Email}}\" }";
Student student = new Student();
student.Name = "Parker";
student.Email = "Parker#xyz.com";
student.Address = "Mark Avenue";
var result1 = GenerateJson(formula, student);
//Output : "{\"Name\": \"Parker\", \"Email\": \"Parker#xyz.com\" }"
student.Name = "Royal";
student.Email = "Royal#xyz.com";
student.Address = "Cross Lane";
var result2 = GenerateJson(formula, student);
//Output : "{\"Name\": \"Royal\", \"Email\": \"Royal#xyz.com\" }"
public string GenerateJson(string formula, Student student)
{
string result = "";
//logic for replacing the Placeholder woth object properties
return result;
}
class Student
{
public string Name { get; set; }
public string Email { get; set; }
public string Address { get; set; }
}
If you really don't want or cannot use Json.NET than you can try solution below
public string GenerateJson(string formula, Student student)
{
return Regex.Replace(formula, #"\{\{(\w+)\}\}", match => typeof(Student).GetProperty(
match.Groups[1].ToString())?.GetValue(student)?.ToString());
}
You can deserialize it to ExpandoObject (IDictionary<string,object>). Then compare property names with the known type. If there is match between Dictionary's key and student's propertyName. Replace ExpandoObject's Value with Student's property's value. After all, serialize it to json.
Here it is,
public string GenerateJson(string formula, Student student)
{
IDictionary<string, object> templateValues = JsonConvert.DeserializeObject<IDictionary<string, object>>(formula);
PropertyInfo[] sourceProperty = typeof(Student).GetProperties();
foreach (var item in sourceProperty)
{
KeyValuePair<string,object> value = templateValues.FirstOrDefault(x=> x.Key == item.Name);
if (value.Key != null)
{
templateValues[item.Name] = item.GetValue(student);
}
}
return JsonConvert.SerializeObject(templateValues);
}
It looks like the actual problem is retrieving the value of specific properties to generate an API signature. It's unclear if the signature to sign really needs to be a JSON string or not.
The easiest way is to create an anonymous type with the necessary properties and serialize it, eg :
var payload=JsonConvert.Serialize(new {student.Name,student.Email});
This is far faster than any reflection code and allocates a single extra object only. If you want to use an API with a lot of different request types, it pays to use a code generator or in C# 9, a source generator to generate such calls.
It's possible (but slow) to use reflection to retrieve specific properties, eg with :
var dict=typeof(Student).GetProperties()
.Where(prop=>myProps.Contains(prop.Name))
.ToDictionary(prop=>prop.Name,prop=>prop.GetValue(student));
var json=JsonConvert.Serialize(dict);
A JSON object is actually a dictionary, so serializing a dictionary behaves similarly to serializing an object with the same properties.
Reflection is relatively expensive though, so it's a good idea to cache the PropertyInfo objects you want and reuse them:
Dictionary<Type,PropertyInfo[]> _properties=new Dictionary<Type,PropertyInfo[]>();
...
string GenerateJson<T>(T item)
{
PropertyInfo[] props;
if (!_properties.TryGetValue(typeof(T),out props))
{
props=typeof(Student).GetProperties()
.Where(prop=>myProps.Contains(prop.Name))
.ToArray();
}
var dict=props.ToDictionary(prop=>prop.Name,prop=>prop.GetValue(item));
return JsonConvert.Serialize(dict);
}
I have this json file:
[
{
"blah" : "some text here",
"hidden" : false,
},
{
"blah" : "some other text",
"hidden" : false,
}
]
I load it into a JArray and then I want to use to ToObject method to deserialize the data to a custom Class:
public class LookupItem
{
public string DisplayMember { get; set; }
}
the display member I want it to be the value of the first property that appears on the objects. So that:
var a = myJArray.ToObject(List<LookupItem>);
would return
a[0].DisplayMember ---> some text here
a[1].DisplayMember ---> some other text
I thought I could use a
[JsonProperty(Order = 0)]
attribute but it doesn't seem to be working for deserialization only for serialization. (the real issue is that I don't know the first property's key value upfront).
Inconsitent JSON
If there is no consistency in your JSON, you can deserialize the whole thing to an object.
var items = JsonConvert.DeserializeObject("Your JSON"));
Then cast it to a JContainer and loop through it and create your LookupItem objects. Code below assumes the first property is what you want:
var luItems = new List<LookupItem>();
var item = ((Newtonsoft.Json.Linq.JContainer)items).ToList()[0];
((Newtonsoft.Json.Linq.JContainer)items).ToList().ForEach(x =>
luItems.Add(new LookupItem { DisplayMember = x.First.First().ToString() }));
Consitent
If there is consitency, then create a C# class to represent objects of your JSON:
public class Class1
{
public string blah { get; set; }
public bool hidden { get; set; }
}
Then deserialize and create LookupItem instances:
var consitentItems = JsonConvert.DeserializeObject<Class1[]>(File.ReadAllText("Files/Json.txt"));
var consistentLookupItems = new List<LookupItem>();
consitentItems.ToList().ForEach(x =>
consistentLookupItems.Add(new LookupItem { DisplayMember = x.blah }));
using Newtonsoft.Json.Linq.JArray
take All JTokens
Newtonsoft.Json.Linq.JArray jarr = (Newtonsoft.Json.Linq.JArray)(JsonConvert.DeserializeObject(yourJsonScript));
List<LookupItem> myList= New List<LookupItem>();
jarr.ToList().ForEach(jtoken =>
{
myList.Add(new LookupItem(){DisplayMember=jtoken.ElementAt(0).ToObject<string>()});
});
I have this class
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
}
I have a normal ListBox. Im selecting Data from MySql.
private void ListUsers(string server)
{
List<UserData> ls = new List<UserData>();
foreach(dynamic obj in _data)
{
if(obj.servername == server)
{
ls.Add(new UserData() { Name = obj.username, Val = obj.password });
}
}
UserList.Sorted = true;
UserList.DisplayMember = "Name";
UserList.ValueMember = "Val";
UserList.DataSource = ls;
}
When debugging the ls it contains
[0]
Name => "Test",
Val => "12345"
[1]
Name => "Test2",
Val => "54321"
Now sometimes there ist only 1 postition in that list. If this happens, I want to select that entry or at least the Name and paste this into a textbox.
But for some reason I cant achive this. And Google didnt brought any results. At least non that suites to my problem.
I tried
rdpUserList.Items[0].ToString();
but this brings me ProjectName.UserData and not Test.
What is the right way to select the first Item in a list that was generated by a datasource ?
You're calling ToString() on an instance of the type ProjectName.UserData, which gives you its type name.
You want to access that instance's Name property instead.
If rdpUserList is a List<UserData>, you want this:
rdpUserList.Items[0].Name
If instead it's a datasource, you need to cast the item in order to access its properties:
((ProjectName.UserData)rdpUserList.Items[0]).Name
There are two approaches to this issue. The first is a logical problem; you need to call the Name property and not the ToString() method. Note that using this way, you need to cast to your object type.
((UserData)rdpUserList.Items[0]).Name
The second option is to override the ToString() method so you can call the name the way you tried.
class UserData
{
public UserData() { }
public string Name { get; set; }
public string Val { get; set; }
public override string ToString()
{ return this.Name; }
}
and then call with
rdpUserList.Items[0].ToString()
Sorry for previous post, I did not test before I answered,
try the following if you want, I just like linq :)
var item = rdpUserList.Items.OfType<ListItem>().First();
Class1 t = new Class1(){Id=Convert.ToInt32(item.Value), description = item.Text};
OR for just the name
string name = rdpUserList.Items.OfType<ListItem>().First().Text;
I made a class for deserialize a JSON (I'm using JSON.net), this is the structure:
public class User
{
public string Name { get; set; }
public string Surname { get; set;}
}
public class RootObject
{
public List<User> Users { get; set; }
}
I deserialize the JSON like this:
var obj = JsonConvert.DeserializeObject<RootObject>(responseText);
Now If I want return all the JSON I simply do: return obj.Users; but how can I return a specific property of the class User? In particular the Name property. I tried this:
return obj.Users.name // user list does not contain any name property
Also I tried with LINQ:
obj.Select(c => c.Name).Single();
But I can't use Select because I need to declare obj as:
var obj = JsonConvert.DeserializeObject<List<RootObject>>(responseText);
If instead I return obj.user; I can use LINQ and access to the name. But how can do this in the method that return the response?
Add a reference to class
using System.Linq;
since user is of type List you can apply lambda expression in it. Do not use Single it will fail if user will have more than one record in it, use FirstOrDefault instead. It will work for zero or more number of elements
var obj = JsonConvert.DeserializeObject<RootObject>(responseText);
string name = obj.user.Select(p=> p.name).FirstOrDefault();
string surname = obj.user.Select(p=> p.surname).FirstOrDefault();
You have to use one user instead of the whole list as RootObject.user is a List<User> and not an instance of User:
var name = obj[0].Name;
Or whatever element within your list you want to use.
Alternativly use LINQ:
var user = obj.FirstOrDefault();
if (user != null) name = user.name;
I have a JSON "multi-level" response that I need to deserialize and from the deserialized classes structure I need to extract all the objects of a certain class.
Below the code I'm using, at the end I find that my result is empty, not populated.
// given these two classes:
[DataContract]
public class ThingsList
{
[DataMember(Name = "status")]
public string Status { get; set; }
[DataMember(Name = "since")]
public double Since { get; set; }
[DataMember(Name = "list")]
public Dictionary<string, ThingsListItem> Items { get; set; }
public DateTime SinceDate { get { return UnixTime.ToDateTime(Since); } }
}
[DataContract]
public class ThingsListItem
{
[DataMember(Name = "url")]
public string Url { get; set; }
[DataMember(Name = "title")]
public string Title { get; set; }
}
// I can deserialize my json to this structure with:
ThingsList results = JsonConvert.DeserializeObject<ThingsList>(e.Result);
// now I need to "extract" only the ThingsListItem objects, and I'm trying this:
var theList = from item in results.Items.OfType<ThingsListItem>()
select new
{
Title = item.Title,
Url = item.Url
};
// but "theList" is not populated.
The points here are (I believe):
- I try to use results.Items.OfType() in order to extract only the ThingsListItem objects, that in the "upper" class are declared in the
public Dictionary Items { get; set; }
row.
Any idea? Tell if it's not clear...
Thanks
Andrea
EDIT: updated my response for clarity.
Since your Dictionary values are of type ThingsListItem you can access them directly by using the Dictionary's Values property. There is no need to use OfType to check their type and extract them. Simply use:
var items = results.Items.Values;
The Values property would return an ICollection<ThingsListItem>. You can then iterate over the results with a foreach. LINQ does not have to be used.
While the Values property described above should be sufficient, I will point out a few issues with your original LINQ query attempt.
1) The following query is probably what you were after. Again, the Dictionary's Values property is key (no pun intended) to accessing the items:
var theList = from item in results.Items.Values
select new
{
Title = item.Title,
Url = item.Url
};
2) Why are you using new? That will return an IEnumerable of anonymous types. You already have a defined class, so why project into a new anonymous type? You should retain the underlying ThingsListItem items by selecting the item directly to get an IEnumerable<ThingsListItem>:
var theList = from item in results.Items.Values
select item;
foreach (var item in theList)
{
Console.WriteLine("Title: {0}, Url: {1}", item.Title, item.Url);
}
You would usually project into a new anonymous type to define a type with data properties you are interested in. Generally you would use them immediately after the query, whereas a selection into an existing class could be used immediately or passed around to other methods that are expecting that type.
Hopefully this has cleared up some questions for you and you have a better idea of using LINQ and when to use the new keyword. To reiterate, for your purposes it seems the Values property should suffice. Using LINQ to select the item is redundant when there are other immediate means to do so.