How to convert object into json array without property names - c#

For this class
class Customer {
public string FirstName {get; set;}
public string LastName {get; set;}
}
I have collection
List<Customer> customers
When returning to browser client
return new JsonResult(new
{
data = customers
});
The client get
{"data":[{"firstName":"Johny","lastName":"Johnson"}]}
Is there some way to get
{"data":[{"Johny","Johnson"}]}
without doing foreach like
var output = new List<string[]>();
foreach (var r in customers)
{
output.Add(new string[] {
r.FirstName,
r.LastName
});
}
?

You could add another property in the Customer object,
public string[] FullName {get { return new string[]{FirstName, LastName}; } }
Decorate your Firstname and LastName properties with [JsonIgnore] so they don't get serialized.
Final product would look like so
public class Customer{
[JsonIgnore]
public string FirstName{get;set;}
[JsonIgnore]
public string LastName{get;set;}
[JsonProperty("data")]
public string[] FullName {get { return new string[]{FirstName, LastName}; } }
public Customer(string FirstName, string LastName){
this.FirstName = FirstName;
this.LastName = LastName;
}
}
public static void Main(string[] args)
{
Customer c = new Customer("Adrian", "i6");
Console.Write(JsonConvert.SerializeObject(c));
}
This of course won't return exactly the desired result, if you wanted to completely remove the property you'll have to override the JsonWrite method inside JsonConverter however that would cause the JSON to be invalid of course as the JSON object requires a key:value property.
DotNetFiddle runnable of the above.

If you want some sort of "automatically derive a table from an array of JSON objects" functionality that's general across any data type, the algorithm would be to:
Iterate over the array, collecting newly-encountered property names into a list as you go into column names. This is the only way to get all property names since it may not be guaranteed that all objects have the same properties in JSON.
Create a list for each object in the list
Map each object's property value into the list index of the column corresponding to the property name
This will give you two output artifacts: the column listings and the values by index. If you are safe assuming that the first object has the same properties as all the other objects in the Array, then you can avoid iterating over the entire collection for the first step. This is untested, but hopefully you get the gist.
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
// ...
var payload = JObject.Parse(rawJson);
var dataArray = payload["data"] as JArray;
var firstItem = dataArray[0] as JObject;
var columns = firstItem.Properties().Select(prop => prop.Name).ToList();
var rows = (
from obj as JObject in dataArray
select columns.Select(col => obj[col]).ToList()
).ToList();

Related

Convert List<string> to Model class

Having issue with converting List to Student object in c#.
I have a C# model class as below:
public class Student{
public int ID{get;set;}
public string StudentName{get; set;}
public string StudentAddress{get; set;}
public string StudentRemarks{get; set;}
public string AdditionalInfo{get;set;}
}
I have another class where I have a List which holds data as below (Since this is just a list of string, it won't have any property names in front of it such as 'ID: 001') Note: This string will not have any data for 'AdditionalInfo'.
001
John, Snow
NewYork
Sample test info
Now I have another class where I wanted to convert this List to my 'Student' class where 001 has to be assigned to ID, John Snow has to be assigned to 'StudentName', NewYork has to be assigned to 'StudentAddress', Sample test info has to be assigned to 'StudentRemarks'. Since this doesn't have any data provided for 'AdditionalInfo' property, this should be assigned with empty or null value in it. Here is the class
public class StudentInfoService
{
public List<string> GetStudentInfo(string data)
{
var studentData = new List<string>();
foreach (var line in File.ReadLines(data))
{
if (!string.IsNullOrWhiteSpace(line))
{
var data = line.Split('|');
foreach (var item in data)
{
studentData.Add(item);
}
studentData.ConvertAll(c => (Student)c); //Here is where I am struggling to convert the list string to model class
}
}
return studentData ;
}
}
The issue is, I want to convert the list to 'Student' object and automatically assign all the data to the respective properties by order(there won't be any null or empty data in between other than the 'AdditionalInfo'). Student object will have only one student record. It won't have a list of records. So I need to convert the List to Student object. Please help me with this.
You will need to write code to map lines of text to a model instance, e.g.
public Student GetStudent(List<string> list)
{
return new Student
{
ID = int.Parse(list[0]),
StudentName = list[1],
StudentAddress = list[2],
StudentRemarks = list[3],
AdditionalInfo = (list.Count > 4) ? list[4] : null
};
}

Replace template Placeholder with Object Properties with Reflection

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);
}

Can't return specific nested class property

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;

How to extract an object name

i've got a class filled with lists of subclasses:
public class ClassOfKb
{
public List<Data> KbDatas {get;set;}
public List<Product> KbProducts {get;set}
}
public class Data
{
public Guid ID {get;set;}
public byte[] data {get;set;}
public string Name {get;set;}
}
public class Product
{
public Guid ID {get;set;}
public string Name {get;set;}
public byte[] Image {get;set;}
}
i create an object:
ClassOfKb kb = new ClassOfKb
now i'd like to extract the string "Datas" from the sub-object kb.KbDatas, I tried:
string name = kb.KbDatas.GetType().BaseType.Name.Substring(2);
aswell as:
string name = kb.KbDatas.GetType().Name.Substring(2);
but nothing gave me what I need, is there any way to do this?
EDIT: to specify my question, the string I need is the name of the list, except the first two letters! KbDatas => Datas
EDIT2: i did a mistake, the list-names and class-names are different and i need the list-name
You can use Type.GetGenericArguments to solve this
ClassOfKb kb=new ClassOfKb();
kb.KbData = new List<Data>();
string nameOfData = Type.GetType(kb.KbData.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfData = Data
kb.KbProduct = new List<Product>();
string nameOfProduct = Type.GetType(kb.KbProduct.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfProduct = Product
Since that's a collection it is likely that there are multiple Data objects in it, each with a name. You can use String.Join to concat them with a separator:
string names = string.Join(",", kb.KbData.Select(d => d.Name));
If there's just one object you don't get a comma at the end. If there's no object you get an empty string.
erm, since you have a List of Data there will be a sequence of Names.
IEnumerable<string> names = kb.KbData.Select(d => d.Name);
maybe you want just the first one?
string firstName = kb.KbData.First(d => d.Name);
Try this one
string name = kb.KbData[0].Name.Substring(2);
From the sounds of what you've written, you're looking to get the name of the type in the List instance KbData?
If so, I think this may be what you're looking for: https://stackoverflow.com/a/1043778/775479
If you are trying to get the name of the property. There are several methods for doing so.
Get the name of the generic argument from the property itself - If you know the name of the property.
ClassOfKb kb = new ClassOfKb()
{ KbData = new List<Data>(), KbProduct = new List<Product>() };
Console.WriteLine(kb.KbData.GetType().GetGenericArguments()[0].Name);
Get the name of the property from reflection, if you know the data type of the property.
System.Reflection.PropertyInfo pi = kb.GetType()
.GetProperties()
.FirstOrDefault(p=>p.PropertyType == typeof(List<Data>));
Console.WriteLine(pi.Name.Substring(2)); // ignoring the kb prefix
You can achieve this with reflection. This is example without any checks - just show the mechanism:
PropertyInfo propertyInfo = typeof(ClassOfKb).GetProperty("KbData");
Type propertyType = propertyInfo.PropertyType;
Type genericArgument = propertyType.GenericTypeArguments[0];
string name = genericArgument.Name;
Because property KbData is generic List<Data> you need ask for generic arguments of property type: propertyType.GenericTypeArguments[0] and you should test if the type is really generic by genericArgument.IsGenericType and check generic arguments count
If you need the property name than you can use Expression.
The code below define function for extract name prom a property:
public string GetPropertyName<T>(Expression<Func<T>> property)
{
return ((MemberExpression)property.Body).Member.Name;
}
This converts property to property name string:
GetPropertyName(()=>k.KbDatas).Substring(2)

Linq extracting objects

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.

Categories