I'm using Newtonsoft.Json lib with C# and encountered a problem. I have a jArray as below:
[{"category": "computer","subcat": "desktop"},
{"category": "category","subcat": "laptop"},
{"category": "television","subcat": "LCD"}]
and I want it to be transformed as a jObject as below:
{"computer": ["desktop","laptop"],
"television": ["LCD"]}
Here want I need is a general method that can handle this type of transformation. Which means this method could also do the trick even if the jArray has a 3rd level property. e.g.
from
[{"typeA":"a","typeB":"a1","typeC":"a11"},{"typeA":"a","typeB":"a1","typeC":"a12"},
{"typeA":"a","typeB":"a2","typeC":"a21"}]
to
{"a":{"a1":["a11","a12"],"a2":["a21"]}}
Thanks a lot!
EXTREMELY quick and dirty - with vast room for improvement, but this will get it done:
Use like this:
var input = #"[{""type"":""a"",""typeB"":""a1"",""typeC"":""a11""},
{""type"":""a"",""typeB"":""a1"",""typeC"":""a12""},
{""type"":""a"",""typeB"":""a2"",""typeC"":""a21""}]";
var b = JsonConvert.DeserializeObject<List<tmp>>(input);
var result = doIt(b, "type", "typeB", "typeC");
With this implementation:
private string doIt(IEnumerable<tmp> objs, params string[] g)
{
var t = CollapseOrGroup(objs,g);
return JsonConvert.SerializeObject(t);
}
private dynamic CollapseOrGroup(IEnumerable<tmp> objs, IEnumerable<string> props)
{
var firstProp = props.FirstOrDefault();
if (firstProp == default(string))
return objs;
var p = Expression.Parameter(typeof(tmp));
var m = Expression.Property(p, firstProp);
var l = Expression.Lambda(m, p).Compile() as dynamic;
if (props.Count() == 1)
{
return Enumerable.Select(objs, l);
} else {
IEnumerable<IGrouping<dynamic, tmp>> g = Enumerable.GroupBy(objs, l);
return g.ToDictionary (o => o.Key, o => CollapseOrGroup(o, props.Skip(1)));
}
}
Prints:
{"a":{"a1":["a11","a12"],"a2":["a21"]}}
Search for Newtonsoft in NuGet and download JSON.Net for this to work
I have encountered the same problem using java.
Currently, the solution from my side is to traversal the initial ArrayJSON, then figure out the wanted format.
You can define Type1 as a model, List as the member variable, same operation with Type2 using Type3, this will help make your code easy to be organised.
Related
I have a scenario that should be really simple but it isnt!
I have a type that is determined at runtime
I want to call
JSonConvert.DeserialiseObject<List<type>>(json);
via Make Generic Method as I need to determine the type at runtime
var entityTypes = _dbContext.Model.GetEntityTypes();
foreach (var entityType in entityTypes)
{
var requiredType = entityType.ClrType;
var testMethod = typeof(JsonConvert).GetMethods().FirstOrDefault(
x => x.Name.Equals("DeserializeObject", StringComparison.OrdinalIgnoreCase) &&
x.IsGenericMethod && x.GetParameters().Length == 1)
?.MakeGenericMethod(requiredType);
var filename = $"my json.json";
var json = File.ReadAllText(filename);
var actualData2 = testMethod?.Invoke(null, new object[] { json });
}
This works perfectly if my json is for a single object
However, this is not the case, my json is for a list obviously the code above wont work because it expects my json to be a single object
So I tried to change to
var requiredType = typeof(List<entityType.ClrType>);
and entityType cannot resolved
What am I doing wrong? Sorry but I find generics really frustrating!
My ideal option was to be able to take a list of objects and convert that into a list of the required type but I cant get that to work in a generic way either hence having to try this
Cheers
Paul
This:
.MakeGenericMethod(requiredType)
change to:
.MakeGenericMethod(typeof(List<>).MakeGenericType(requiredType))
More readable as:
var listType = typeof(List<>).MakeGenericType(requiredType);
and then
...
.MakeGenericMethod(listType)
this is the first time that I come here to try to solve a problem, I've been studying C# for 4 weeks now, so I'm not an expert as you can imagine, so I've been coding since yesterday and I haven't been able to solve an error that keeps appearing no matter what I do "Cannot apply indexing with [] to an expression of type JObject" I hope you can help me.
Here is my code:
private static string GetTorrentUrl(string query)
{
WebClient client = new WebClient();
string json = client.DownloadString(new Uri("https://yts.mx/ajax/search?query=" + query));
Console.WriteLine(json);
JObject result = JObject.Parse(json);
if (result["status"].ToString == "ok")
{
JArray data = (JArray)result["data"];
Console.WriteLine(data[0]["title"].ToString());
}
return "something";
}
I just try it and to it works but the Visual Studio keeps telling me that there's an error.
The problem is that your are trying to get the properties like ["status"] even though you do not know (Or compiler don't know) what that is.
C# is strongly typed so the compiler will detect and flag these errors at compilation time.
If you look at the documentation for JObject you will see that it implements the interface: IEnumerable<KeyValuePair<String, JToken>>:
public class JObject : JContainer, IDictionary<string, JToken>,
ICollection<KeyValuePair<string, JToken>>,
IEnumerable<KeyValuePair<string, JToken>>, IEnumerable,
INotifyPropertyChanged, ICustomTypeDescriptor, INotifyPropertyChanging
Read more at - Documentation
That means you can iterate the list simply by using a loop:
foreach(var item in result)
{
if(item.Key == "status" && item.Value.ToString() == "ok")
// logic here
}
It should also be possible to do it with Linq like this but haven't tested the code:
private static string GetTorrentUrl(string query)
{
WebClient client = new WebClient();
string json = client.DownloadString(new Uri("https://yts.mx/ajax/search?query=" + query));
Console.WriteLine(json);
JObject result = JObject.Parse(json);
var status = result.Where(x => x.Key == "status").First().Value;
if(status == "ok")
{
JArray data = result.Where(x => x.Key == "data").First().Value;
Console.WriteLine(data[0]["title"].ToString());
}
return "something";
}
I have a table called "Account." Account has 3 columns: id, acct_name, is_privileged.
When I write something like "account.", visual studio provides me with a list of attributes/methods I can use. Hence, I get the option of using account.id, account.acct_name, and account.is_privileged.
However, I would like to change a particular column's value dynamically, without typing in my column's name. I am getting the column's name dynamically as a string variable. Is it possible to achieve it? If so, how?
My code is as follows:
set_col_name = rowRule.Cells["setcolumnnameDataGridViewTextBoxColumn"].Value.ToString();
set_col_value = rowRule.Cells["setcolumnvalueDataGridViewTextBoxColumn"].Value.ToString();
foreach (DataGridViewRow rowAcc in dgvAccount.Rows)
{
if (isComparable(rowAcc.Cells[col_name].Value.ToString(), comp_operator, col_value))
{
account.id = (int)rowAcc.Cells["idDataGridViewTextBoxColumn2"].Value;
using (ae = new AccountEntities())
{
var temp = ae.Accounts.SingleOrDefault(a => a.id == account.id);
temp.is_privileged = set_col_value; //learn how to do this dynamically
ae.SaveChanges();
}
}
}
Where I do temp.is_privileged, I'd like to achieve something like, temp."set_col_name" = set_col_value;
Instead of specifying the column name directly as being "is_privileged" in this case, I'd like to pass a string to specify it.
Thank you.
If I understand your problem statement correctly, you want something like this to work:
Account temp = // with temp coming from a library such as EntityFramework
temp.SetValue(set_col_name, set_col_value);
this is quite easy to achieve with either pure reflection or Linq Expression Trees (which I opted for):
static class Ext
{
public static void Set<T, TProperty>(this T instance, string propertyName, TProperty value)
{
var instanceExpression = Expression.Parameter(typeof(T), "p");
var propertyGetterExpression = Expression.PropertyOrField(instanceExpression, propertyName);
//generate setter
var newValueExpression = Expression.Parameter(typeof(TProperty), "value");
var assignmentExpression = Expression.Assign(propertyGetterExpression, newValueExpression);
var lambdaExpression = Expression.Lambda<Action<T, TProperty>>(assignmentExpression, instanceExpression, newValueExpression);
var setter = lambdaExpression.Compile();// the generated lambda will look like so: (p, value) => p.{your_property_name} = value;
setter(instance, value);
}
}
one advantage of this method over pure reflection is that you can build the setter delegate once and call it multiple times at later stage (I will leave this with you to experiment)
with the above in place, hopefully you should be able to do something like this:
var set_col_name = "is_privileged";
var set_col_value = true;
using (ae = new AccountEntities())
{
var temp = ae.Accounts.SingleOrDefault(a => a.id == account.id);
temp.Set(set_col_name, set_col_value);
temp.Set("acct_name", "test");
ae.SaveChanges();
}
You need some reflection in this one. For example
public static void CopyValues<T>(T obj1, T obj2)
{
var type = typeof(T);
foreach (var prop in type.GetProperties())
{
prop.SetValue(obj1, prop.GetValue(obj2));
}
}
And use the above function like this:
var source = new Accounts(){is_privileged = false};
var destiny = new Accounts();
CopyValues(source, destiny);
It depends of what you are loking for, but the key is to use REFLECTION!
In javascript, I often use something like this object creator
var o = new Object(); // generic object
var props = { color:"red",value:5 }; // hashtable
for(var key in props) o[key] = props[key];
alert(o.color); //red
Can it be written as C# method with this declaration?
static void properties(out Object o, HashTable h) { ...
Is this some design pattern? Am I inventing wheel?
You may want to look at the Expando Object in C# 4. That is about as close as you are going to get to a dynamic object in C# like you can get in JavaScript.
http://msdn.microsoft.com/en-us/magazine/ff796227.aspx
http://www.c-sharpcorner.com/blogs/BlogDetail.aspx?BlogId=2134
var test = new { name = "Testobj", color = Colors.Aqua };
MessageBox.Show(test.name);
It's called a anonymous Type, I think this is what you are searching for.
Since c# is statically typed, you cannot achieve this.. closest possible is anonymous methods
Dictionary<string,int> dic=new Dictionary<string,int>();
dic.Add("red", 5);
dic.Add("black", 10);
dic.Add("white", 1);
object[] obj;
foreach(var o in dic)
{
var sam= new { color = o.Key,value=o.Value };
Console.WriteLine(sam.color);
}
I like the approach of having property bag objects (DTOs) which define the interface to my server, but I don't like writing code like this:
void ModifyDataSomeWay(WibbleDTO wibbleDTO)
{
WibbleBOWithMethods wibbleBO = new WibbleBOWithMethods();
wibbleBO.Val1 = wibbleDTO.Val1;
wibbleBO.Val2 = wibbleDTO.Val2;
}
This copying code is quite laborious to write. If the copying code is unavoidable, then where do you put it? In the BO? In a factory? If it is possible to manually avoid writing the boiler plate code then how?
Thanks in advance.
That looks like a job for AutoMapper, or (simpler) just add some interfaces.
This needs more error handling, and you may need to modify it accommodate properties where the data types don't match, but this shows the essence of a simple solution.
public void CopyTo(object source, object destination)
{
var sourceProperties = source.GetType().GetProperties()
.Where(p => p.CanRead);
var destinationProperties = destination.GetType()
.GetProperties().Where(p => p.CanWrite);
foreach (var property in sourceProperties)
{
var targets = (from d in destinationProperties
where d.Name == property.Name
select d).ToList();
if (targets.Count == 0)
continue;
var activeProperty = targets[0];
object value = property.GetValue(source, null);
activeProperty.SetValue(destination, value, null);
}
}
Automapper (or similar tools) might be the way forward here. Another approach may be the factory pattern.
Simplest of all would be something like this:
class WibbleBO
{
public static WibbleBO FromData(WibbleDTO data)
{
return new WibbleBO
{
Val1 = data.Val1,
Val2 = data.Val2,
Val3 = ... // and so on..
};
}
}