Let's say that in my WebAPI I'm receiving a json like this:
{
prop1: "sometext",
prop2: 123,
prop3: true,
prop4: [1,2,3,4,5],
prop5: ["text1", "text2", "text3"]
}
To work with this data, I transform it into this: dynamic data = System.Web.Helpers.Json.Decode(json);. In most cases it's really easy to work with it, but I'm struggling with a situation where I need to repass this data to a database. Most conversions are pretty straightforward, making it easy to iterate over the DynamicJsonObject and send the information to the DB.
My big problem is dealing with properties like prop4 and prop5. This array is being converted to a DynamicJsonArray. To convert it to a primitive array, I'm currently using this:
Array arr = (Array) data["prop4"];
data["prop4"] = arr.Cast<Int32>().ToArray();
The code above works fine and it successfully converts the property value to an integer array that can be sent to the DB. However, when I do it with the string array:
Array arr = (Array) data["prop5"];
data["prop5"] = arr.Cast<string>().ToArray();
The data["prop5"] value isn't changed, and when I inspect it's value, it still points to the type System.Web.Helpers.DynamicJsonArray. Why does this happen?
Also, the thing is that I'm not experienced on working with dynamic objects in C# and I use that because in my situation I do not always know what properties the JSON I'm receiving is going to have. Is there a cleaner way to make this kind of conversion? Actually, is there any need to use dynamic objects to deal with JSONs?
EDIT:
Sorry for being extremely unclear. To clear things up, that's what I'm actually doing:
I'm using a generic function to call a DB procedure with whatever data that comes from the parameter data:
public static IEnumerable<Dictionary<string, object>> CallDBProcedure(string proc, dynamic data)
{
using (NpgsqlConnection conn = new NpgsqlConnection(ConnStr))
{
try
{
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = proc;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
foreach (string property in data.GetDynamicMemberNames())
{
object propertyValue = data[property];
// The method convertNullValue returns the own value if it's not null
// and returns DBNull.Value if it's null. Every value must be properly
// converted, but I can't directly convert a DynamicJsonArray.
cmd.Parameters.Add(new NpgsqlParameter(property, convertNullValue(propertyValue)));
}
var reader = cmd.ExecuteReader();
return new DrToDictionary().Serialize(reader);
}
}
catch (Exception e)
{
throw e;
}
finally
{
conn.Close();
}
}
}
And then, in order to avoid exceptions, I'm processing data before
I pass it to CallDBProcedure, iterating over the DynamicJsonObject
and converting every DynamicJsonArray inside it to a primitive array:
public static void ProcessData(dynamic data)
{
// Directly using data.GetDynamicMemberNames() and modifiying data
// inside the loop causes an exception. Using a string list instead (hacky code?).
List<string> properties = ((Dictionary<string, object>.KeyCollection) data.GetDynamicMemberNames()).ToList();
foreach (string property in properties)
{
object propertyValue = data[property];
if (propertyValue != null && propertyValue.GetType() == typeof(DynamicJsonArray))
{
Array arr = (Array)data[property];
if (arr.Length == 0)
{
// This works.
data[property] = null;
}
else
{
// Discover the type of my array.
if (arr.GetValue(0) is int)
{
// This works.
data[property] = arr.Cast<Int32>().ToArray();
}
else
{
// This doesn't.
data[property] = arr.Cast<string>().ToArray();
}
}
}
}
}
For now I'm iterating over data twice. I know that there must be an obvious and easy way to make this ugly code cleaner, but I'm just not seeing it.
EDIT2:
A solution that I found was to take only the string arrays and put them inside a different variable. Then I can use them normally. I really don't understand why dynamic objects can change their types in some cases and in other cases they can't.
EDIT3:
Forgot to mention that I'm receiving my json like this: dynamic data = System.Web.Helpers.Json.Decode(json);
It is just reading the json object which is returned from the API.
E.g
You can use
dynamic stuff = JsonConvert.DeserializeObject("{ 'prop1' : 'sometext', 'prop2': '123',
'prop3': 'True'}");
string Prop1= stuff.First.Prop1;
string Prop2= stuff.First.Prop2;
or
// For that you will need to add reference to System.Web.Helpers
dynamic json = System.Web.Helpers.Json.Decode(#""{ 'prop1' : 'sometext', 'prop2': '123',
'prop3': 'True'}"");
Console.WriteLine(json.prop1);
another similar quetion in Stackoverflow as below
How can I parse JSON with C#?
Related
I am reading a JSON file and stores the values as a object
Car : Name, Cost, Yearmodel
Now I need to pass these 3 JSON object values to this Testcasedata.
Likewise I need to pass multiple values while reading the JSON file data.
Any idea explaining on how to do this?
public string JSONParser()
{
StreamReader r = new StreamReader("ESAggregationQuery.json");
string jsonString = r.ReadToEnd();
m = JsonConvert.DeserializeObject<AggModel>(jsonString);
carName = m.carName;
costPrice = m.costPrice;
modelYear = m.modelYear;
return "1";
}
private static IEnumerable<TestCaseData> ESAggregativeTestData
{
get
{
yield return new TestCaseData[] { m.carName, m.costPrice, m.modelYear };
}
}
```
You don't "pass" data to your TestCaseData item. The data has to originate there. Try making the following changes...
Call your JSONParser from the getter of ESAggregativeTestData or add the code in there.
Change your yield statement to return a new TestCaseData, not an array. That's not an NUnit thing... it's just how IEnumerable works. If your JSON file, now or in the future, contains multiple test items, you will need to put all this in a loop.
You have not shown us your test code, which uses the data source. To work with the source you have given, it should take three arguments of the appropriate type.
Suggestion... next time provide more complete source code that shows what you are trying to do.
I have a c# class object that is List<int,string> I need to make sure the list's string variable is HtmlEncoded before passing back to an MVC controller (where I will later need to decode) but I'm having difficulty because the company's custom libraries are written in such a way that I have a hard time manipulating the code.
In this particular scenario, the data field is embedded into a SQL statement that is converted to the List<int,string> object. Before this List<int,string> is passed back, I need to use WebUtility.HtmlEncode on the string variable "GroupName":
public List<DatasetModel> GetMembers(List<int> groupIds)
{
using (var work in unitOfWorkFactory())
{
return work.DataContext.Query.Read<DatasetModel>(
$#SELECT {nameof(TableOne.GroupId)
, {(nameof(TableOne.Name))} as GroupName
FROM {this._tableProvider.GlobalTable<TableOne>()}
WHERE {nameof(TableOne.GroupId)} {groupIds.ToSqlInClause()}"
).ToList();
)
}
}
I've tried creating extension methods and something like the following but get type conversion errors, etc, that lead me to write rudimentary for loops and cast() and such that are not appropriate:
List<DatasetModel> returnList = model.Select(x => new DatasetModel{x.GroupId, WebUtility.HtmlEncode(x.Name)}).ToList();
FOLLOW-UP:
Here is what I've had to do to achieve the goal. I don't like it because I am using a foreach loop in the called method. I haven't been able to consistently understand / resolve errors involving 'initializer errors' and List vs generic lists:
public List<DatasetModel> GetMembers(List<int> groupIds)
{
using (var work in unitOfWorkFactory())
{
return EncodeList(work.DataContext.Query.Read<DatasetModel>(
$#SELECT {nameof(TableOne.GroupId)
, {(nameof(TableOne.Name))} as GroupName
FROM {this._tableProvider.GlobalTable<TableOne>()}
WHERE {nameof(TableOne.GroupId)} {groupIds.ToSqlInClause()}"
).ToList();
)
}
}
private static List<DatasetModel> EncodeList(List<DatasetModel> list)
{
var returnList = new List<DatasetModel>();
foreach (var l in list)
{
var m = new DatasetModel(attributeId: l.attributeId, attributeName: WebUtility.HtmlEncode(l.attributeName));
returnList.Add(m);
}
return returnList;
}
I've since getting a bunch of help from the SO hivemind been able to create a REST API (C#, MVC) that handles dynamic (at least that's what I want to call it) calls through catching the string param and finding the correct method through using reflection.
var myType = typeof(JaberoDC.JaberoDC.JaberoDC);
var method = myType
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Single(mi =>mi.ReturnType == typeof(DataSet)
&& string.Equals(mi.Name, param, StringComparison.OrdinalIgnoreCase));
var subject = Activator.CreateInstance(myType);
var result = method.Invoke(subject, new Object[] { "", conStr, "", 0, 0, null });
DataSet ds = (DataSet)result;
What I need help with now is any sort of advice on how to handle the various results dynamically. Meaning that I need some help with understanding how to either create a class that handles 0=>N columns from the rows in the DataTable (after getting the DT from the DS), or how to serialize the data without creating instances of above mentioned class.
Again, I'm sorry if my terminology is off here.
I've basically been thrown into the deep-end at my current employer to create a REST API for a DataComponent that has a few hundred methods. As it is now the API only works when using below code:
List<Worksite> arr2 = new List<Worksite>();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
string id = row["JobID"].ToString();
string name = row["JobName"].ToString();
string site = row["SiteName"].ToString();
arr2.Add(new Worksite
{
ID = id,
JobName = name,
SiteName = site
});
}
}
Basically this only handles worksites (a specific DataSet in the DataComponent).
My current solution:
public string GetFromParam(String param)
{
var myType = typeof(JaberoDC.JaberoDC.JaberoDC);
var method = myType
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Single(mi =>mi.ReturnType == typeof(DataSet)
&& string.Equals(mi.Name, param, StringComparison.OrdinalIgnoreCase));
var subject = Activator.CreateInstance(myType);
var result = method.Invoke(subject, new Object[] { "", conStr, "", 0, 0, null });
DataSet ds = (DataSet)result;
List<GenerateModel> arr2 = new List<GenerateModel>();
int count = 0;
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
Dictionary <string, object> dicRow;
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
dicRow = new Dictionary<string, object>();
foreach (DataColumn col in table.Columns){
dicRow.Add(col.ColumnName, row[col]);
}
rows.Add(dicRow);
}
}
// for (int i = 0; i < ds.Tables.)
string json = JsonConvert.SerializeObject(rows);
return json;
}
I'm basically using the proposed solution in the marked answer whilst also using a dynamic solution to the issue with datakey-value pairs by using a dictionary to hold them.
I agree with Clint's comment about using Json.net by Newtonsoft. That has a lot of helper methods that will make your life easier.
Ideally, you will be creating one controller per type of data that you expect to receive. Then you can use the incoming data to create an instance of a class that is modeled after what you will be receiving. With RESTful APIs and MVC, I treat the incoming data as a FormDataCollection, which is like an incoming stream of keyvalue pairs that you must process sequentially. Here is a code sample to point you in the right direction:
// POST api/mycontrollername
public HttpResponseMessage Post(FormDataCollection fdc)
{
try
{
if (fdc != null)
{
MyClass myInstance = new MyClass();
IEnumerator<KeyValuePair<string, string>> pairs = fdc.GetEnumerator();
while (pairs.MoveNext())
{
switch (pairs.Current.Key)
{
case "PhoneNumber":
myInstance.PhoneNumber = pairs.Current.Value;
break;
...
default:
// handle any additional columns
break;
}
}
// do stuff with myInstance
// respond with OK notification
}
else
{
// respond with bad request notification
}
}
catch (Exception ex)
{
// respond with internal server error notification
}
}
If you must handle multiple data types with the same controller, there may be a particular data field that would give you a clue as to what you are receiving so that you can handle it appropriately. If you genuinely have no idea what you are receiving at any time, you may be able to cast it as a dynamic type and use reflection, but it sounds like the data sender is setting you up for a tough integration in that case - it is definitely not how I would engineer an API...
EDIT: (To clarify based on your comments) It appears you want to accept requests for any number of data types using a single controller, and return a DataTable with the results, even though you don't know in advance which fields the DataTable will contain.
First, unless it is a specific requirement, I wouldn't use a DataTable because it is not a very platform-independent solution - if the requesting application is in a non-.NET language, it will have an easier time parsing a Json array compared to a DataTable. If you must use a DataTable, you can look into .Net's DataTable.ToXML() extension method, but I have run into issues with special characters not converting to XML well and some other gotchas with this approach.
If you want to go the recommended json route, the data you request will have to be sufficient to query the database (ie: RequestType = "jobSiteReport", MinDate = "7/1/2016", MaxDate = "7/12/2016"). Use this to query the database or generate the data (into a DataSet or DataTable if that is what you are comfortable with) depending on what the "RequestType" is. Use Linq or a loop or some other method to transform the DataTable into an object array (this could be an anonymous type if you want, but I prefer keeping things strongly typed when I have the option.) This is pseudocode, but should give you an idea of what I mean:
//using Newtonsoft.Json;
List<MyObject> objects = new List<MyObject>();
for (int i = 0; i < dt.Rows.Count; i++)
{
MyObject obj = new MyObject()
obj.Time = dt.Rows[i]["Time"];
obj.Person = dt.Rows[i]["PersonID"];
...
objects.Add(obj);
}
string json = JsonConvert.SerializeObject(objects.ToArray());
Now you have a string that contains json data with all of the results from your DataTable. Then you can return that. (You could also use a SqlDataReader to accomplish the same result without using a DataTable as an intermediate step - populate the list from the SqlDataReader, convert the list to an array, then serialize the array to json.)
If you are inventing the schema, you should create documentation about what the requester should expect to receive.
Json.Net also has methods for deserializing strongly typed objects from json, deserializing anonymous types, etc. You should read up on what is available - I'm sure that it has something that will be perfect for what you are trying to achieve.
I am having a lot of trouble on my code, I am trying to make some sort of parser. Basically I am trying to get data from a JSON file, and use that data to call methods from my code.
Here's a basic example of my JSON file
{ "story": { "Talk": [ "me", 1, 1 ] } }
Now I have a class called DialogueSystem, it contains a function called Talk with three parameters, string, int, int.
I am using SimpleJSON to get my JSON data, but I am guessing it's similar to other JSON parsers.
Also, I have other functions which have different parameters which is why I am forced to use reflection
Anyways, here's the code that gets the json data, and tries to use Reflection to call the Talk method.
// Gets the JSON and parses it
JSONNode Node = JSONNode.Parse(jsonFile());
var method = sys.GetType().GetMethod(""); // Reflection stuff
foreach (JSONNode item in Node["story"].Keys) // the Keys just gives me every key that's in the story node/key
{
List<object> parameters = new List<object>(); // List containing the parameters, to be used when invoking the method
for (int i = 0; i < Node["story"][item].Count; i++)
{
//This part tests if it's a string or int and adds it as such
string data = Node["story"][item][i];
int n;
bool isNum = int.TryParse(data, out n);
if (isNum)
{
parameters.Add(n);
}
else
{
parameters.Add(data);
}
}
// Invoke the method using it's method name and the method parameters
method.Invoke(item, parameters.ToArray());
}
Btw, my Talk class just prints a text based on the given input.
For some reason, I get this error
NullReferenceException: Object reference not set to an instance of an
object Dialogue.StoryTeller.ReadStory (Dialogue.DialogueSystem sys)
(at Assets/Dialogue System/Scripts/StoryTeller.cs:57)
If you have any idea how to fix it, or maybe make it better, that would be awesome!
Thanks!
Your .getmethod call is using an empty string. Therefore your methodinfo object is null. Hence the exception. https://msdn.microsoft.com/en-us/library/8zz808e6(v=vs.110).aspx
Ok, after searching everywhere on the Microsoft docs and stack overflow, I found out that I needed to change two things.
First of all I had to change var method = sys.GetType().GetMethod("");
to this var method = sys.GetType().GetMethod(item.ToString().Replace("\"", ""));
(Which I also moved inside the for loop in order to use item)
Also, I needed to change the invoke method.Invoke(item, parameters.ToArray()); to this method.Invoke(sys, parameters.ToArray());
So the whole code is now this
Node = JSONNode.Parse(jsonFile());
foreach (JSONNode item in Node["story"].Keys)
{
List<object> parameters = new List<object>();
var method = sys.GetType().GetMethod(item.ToString().Replace("\"", ""));
for (int i = 0; i < Node["story"][item].Count; i++)
{
string data = Node["story"][item][i];
int n;
bool isNum = int.TryParse(data, out n);
if (isNum)
{
parameters.Add(n);
}
else
{
parameters.Add(data);
}
}
method.Invoke(sys, parameters.ToArray());
}
EDIT AGAIN: the solution was probably different from my original question. Thanks everyone very much your great ideas. I wish I could vote for more than one answer.
EDIT: I am populating a Jquery table plugin from datatables/.net and it requires the data (Json) to be in a certain format like this;
"sEcho": 1,
"iTotalRecords": 57,
"iTotalDisplayRecords": 57,
"aaData": [
[
"Gecko",
"Firefox 1.0",
"Win 98+ / OSX.2+",
"1.7",
"A"
],
[
"Gecko",
"Firefox 1.5",
"Win 98+ / OSX.2+",
"1.8",
"A"
],
...
]
}
I am recieving data from a service that is returning a collection of object. I would like one method which I can pass these collections into and it will return the appropriate string
thanks
END EDIT
I would like to build a method that can receive and object that we build and will return an array List each containing the value of each object passed in. For example;
I have a collection of 'Car' objects
What I would like to do is
public object[] Something<T>(_cars)
{
object[] objArray = new object[_cars.Count];
foreach(Car _car in _cars ){
IList objList = new List<string>;
objList.Add(_car.Color);
objList.Add(_car.EngineSize);
//etc etc
objArray[i] = objList;//i'll have to use a for loop to get the i counter but you get my idea
}
return objArray
}
my problem is how can I access the properties of the object without knowing what type of object it is?
thanks for any help
Update: To answer your revised question - produce a JSON result of a data structure - use the built-in JavaScriptSerializer class:
JavaScriptSerializer serializer = new JavaScriptSerializer();
string json = seriaizer.Serialize(myObjectOrArray);
Below is the previous answer.
how can I access the properties of the object without knowing what type of object it is
Using Reflection, and grabbing the properties which are strings. Note this is not necessarily a good idea. The fact that you have to use reflection to get what you want is usually a HUGE WARNING FLAG that your design is wrong.
However, in the hopes of learning something useful, here's how it could be done:
public object[] Something<T>(T[] items)
{
IList objList = new List<object>();
//get the properties on which are strings
PropertyInfo[] properties = typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string));
foreach(T item in items)
{
IList stringList = new List<string>;
foreach(PropertyInfo property in properties)
{
objList.Add(property.GetValue(item, null) as string);
}
objList.Add(stringList);
}
}
return objList.ToArray();
}
A far, far better solution would be to require all the objects coming into this method to conform to some interface that requires them to provide their own string-formatted data. Or maybe take two steps back and ask for help on the underlying problem. This approach is fraught with problems. It's a rabbit hole you don't want to go down.
Use the System.Web.Script.Serialization.JavaScriptSerializer class. It was specifically provided for JSON serialization.
using System.Web.Script.Serialization;
public string ToJson(object o)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(o);
}
EDIT: Oops, I missed that your plugin doesn't want a true JSON representation of the objects; it just wants arrays of values. You could use reflection to iterate over the properties of the objects as others have suggested, but then you have no control over which properties end up in which columns. It is not clear from your question whether that is a problem for you.
If you need a strict mapping between properties and columns, then you will have to define that somehow in C#. To do this you could implement IEnumerable as Ed demonstrates or create a custom interface:
public interface ITableDataSource
{
IList<string> GetTableData();
}
Then implement this on any objects that might need to be data sources for the jQuery table plugin:
public class Car : ITableDataSource
{
//...class implementation details...
public IList<string> GetTableData()
{
return new List<string>()
{
this.Color,
this.EngineSize,
this.NumberOfSeats.ToString()
};
}
}
Finally, in your method that is returning the data to the jQuery plugin, use the interface to construct your response object, then pass it to my ToJson() method to serialize it:
public string DoSomething(IList<ITableDataSource> objects)
{
var result = new
{
sEcho = 1,
iTotalRecords = 1,
iTotalDisplayRecords = 1,
aaData = new List<IList<string>>()
};
foreach (ITableDataSource ds in objects)
result.aaData.Add(ds.GetTableData());
return ToJson(result);
}
While it would be relatively straightforward to use reflection to loop through all of your objects and all the properties on those objects to build that string, it's already been written.
I would highly recommend looking at the Json.NET project found here. Add this DLL to your project and converting a list of objects into a Json formatted string is as easy as:
string json = Newtonsoft.Json.JsonConvert.SerializeObject( listOfCars );
IList objList = new List<string>();
foreach ( PropertyInfo prop in _car.GetType().GetProperties() )
{
var value = prop.GetValue( _car, null );
objList.Add( value != null ? value.ToString() : null );
}
objArray[i] = objList;
The code to get the Property values of an object is
foreach (PropertyInfo info in myObject.GetType().GetProperties())
{
if (info.CanRead)
{
object o = propertyInfo.GetValue(myObject, null);
}
}
my problem is how can I access the properties of the object without knowing what type of object it is?
Well, in the general sense you can't. Generics is all about treating objects generically. However, you can impose type constraints by using a where clause. Based on the foreach loop I would say constrain your types to types that implement IEnumerable, but then you go on to use properties like "Color" and "EngineSize", which are very specific. I don't even know why you would have properties named "Color" and "EngineSize" that are strings, but that is another problem altogether...
It seems like the best approach for you would be to define an interface or an abstract base class that each of these objects inherits from. Then you can use the 'where' clause to constrain to objects of that interface/base class only So...
public object[] Something<T>( T _cars) where T : IEnumerable<MyInterface>
However, if we are going to go down this road I don't see why the method should be generic at all. It could simply take an IEnumerable<T> as an input. When we only want to use one type in a method generics is not needed.