I got the following problem with my data stream:
The data stream consists of a dictionary, which I want to parse and specify the type of the value dynamically.
I.e. my data strea includes:
"date", "01.01.2000"
"name", "joe"
"alive", "true"
"health", "100"
Now I would like to set my properties of a generic class according to this data stream:
class GenericClass
{
Hashtable genericAttributes;
}
Is there a possibility to set my values of the data stream to the correct type via reflection?
I could try something like:
DateTime.TryParse(date, out myDate);
for the date time object, but I don't think this will work when trying to parse doubles, floats, int16s, int32s, uint16,...
Some thoughts on that?
Thx and regards
My guess from your question is that they all are IConvertible, so I would do something my code example below. The idea is that I specify the "want"-order, ie in the order I want the types if they can fit multiple types, and then I try to convert them in that order.
public class GenericPropClass
{
public Type type;
public object value;
public string key;
}
[TestMethod]
public void PropertySet()
{
var dict = new Dictionary<string, string>();
var resultingList = new List<GenericPropClass>();
// Specify the order with most "specific"/"wanted" type first and string last
var order = new Type[] { typeof(DateTime), typeof(int), typeof(double), typeof(string) };
foreach (var key in dict.Keys)
foreach (var t in order)
{
try
{
var res = new GenericPropClass()
{
value = Convert.ChangeType(dict[key], t),
key = key,
type = t,
};
resultingList.Add(res);
break;
}
catch (Exception)
{
// Just continue
}
}
}
Sorry for a short answer containing almost only code, I might have time to improve it tonight to get my thoughts in, but I have to go now :)
Related
I'm using mastercard API. Link https://developer.mastercard.com/documentation/mastercom/6#api_transactions_debit_master_card_and_europe_dual_acquirer
I Have to get the value of these keys. (These are key from Dictionary List)
transactionSummaryList[0].authTransactionId
transactionSummaryList[0].clearingTransactionId
transactionSummaryList[0].singleMessageSummaryDetails.authTransaction.acquirerReferenceNumber
transactionSummaryList[0].singleMessageSummaryDetails.authTransaction.adviceReasonCode
...
My code so far
try
{
TransactionsDebitMasterCardAndEuropeDualAcquirer response = TransactionsDebitMasterCardAndEuropeDualAcquirer.searchForDebitMCMessageTransaction(transactionsValues);
OutputTransaction outputTransaction = new OutputTransaction();
foreach (Dictionary<String, Object> item in (List<Dictionary<String, Object>>)response["transactionSummaryList"])
{
TransactionSummary transactionSummary = new TransactionSummary();
transactionSummary.authTransactionId = Out(item, "authTransactionId").ToString();
transactionSummary.clearingTransactionId = Out(item, "clearingTransactionId").ToString();
Object aux = Out(item, "singleMessageSummaryDetails");
outputTransaction.transactionSummaryLists.Add(transactionSummary);
}
outputTransactions.Add(outputTransaction);
}
public static Object Out(Dictionary<String, Object> response, String key)
{
return response[key];
}
I'm facing trouble when i try to get values from singleMessageSummaryDetails Key.
All values that I want is inside Object aux, but i can't "reach" them.
You have case aux as an Object, therefore it doesn't know about any specific properties. Object is the base class that all other .NET classes inherit from.
If you want to get to a specific property they you'll need to cast your object to a specific type.
If you know the class type, then you can cast it directly using either;
public static YourClass Out(Dictionary<String, YourClass > response, String key) or MyClass myClassInstance = (YourClass)myObject;
If you don't know the type then you can use reflection. Its a bit more complicated but will be something like;
Type myType = myObject.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(myObject, null);
// Do something with propValue
}
I finally got it!
To make this work, first I turn the mastercard answer in a JSON.
Them I put it simple and easy in my object (DeserializeObject).
try
{
TransactionsDebitMasterCardAndEuropeDualAcquirer response = TransactionsDebitMasterCardAndEuropeDualAcquirer.searchForDebitMCMessageTransaction(transactionsValues);
OutputTransaction outputTransaction = new OutputTransaction();
var aux = JsonConvert.SerializeObject(response);
outputTransaction = JsonConvert.DeserializeObject<OutputTransaction>(aux);
outputTransactions.Add(outputTransaction);
}
Thanks so much for your answer #Mark Cooper, helps me a lot.
Sry for my bad english.
Thanks again.
I'm using JSON.NET to deserialize a JSON file to a dynamic object in C#.
Inside a method, I would like to pass in a string and refer to that specified attribute in the dynamic object.
For example:
public void Update(string Key, string Value)
{
File.Key = Value;
}
Where File is the dynamic object, and Key is the string that gets passed in. Say I'd like to pass in the key "foo" and a value of "bar", I would do:
Update("foo", "bar");, however due to the nature of the dynamic object type, this results in
{
"Key":"bar"
}
As opposed to:
{
"foo":"bar"
}
Is it possible to do what I'm asking here with the dynamic object?
I suspect you could use:
public void Update(string key, string Value)
{
File[key] = Value;
}
That depends on how the dynamic object implements indexing, but if this is a Json.NET JObject or similar, I'd expect that to work. It's important to understand that it's not guaranteed to work for general dynamic expressions though.
If you only ever actually need this sort of operation (at least within the class) you might consider using JObject as the field type, and then just exposing it as dynamic when you need to.
Okay so it turns out I'm special. Here's the answer for those that may stumble across this in future,
Turns out you can just use the key like an array index and it works perfectly. So:
File[Key] = Value; Works the way I need as opposed to
File.Key = Value;
Thanks anyway!
You can do it, if you're using JObject from JSON.NET. It does not work with an ExpandoObject.
Example:
void Main()
{
var j = new Newtonsoft.Json.Linq.JObject();
var key = "myKey";
var value = "Hello World!";
j[key] = value;
Console.WriteLine(j["myKey"]);
}
This simple example prints "Hello World!" as expected. Hence
var File = new Newtonsoft.Json.Linq.JObject();
public void Update(string key, string Value)
{
File[key] = Value;
}
does what you expect. If you would declare File in the example above as
dynamic File = new ExpandoObject();
you would get a runtime error:
CS0021 Cannot apply indexing with [] to an expression of type 'ExpandoObject'
In SharePoint Server side code, you can write something like:
field.fieldvalueType
Which will sometimes give you the type (DateTime, or whatever). Annoyingly, sometimes, it just returns Null (for example, the ID field).
In CSOM, you don't have that field. However, there's TypeAsString which gives SharePoint types such as:
Computed
Integer
Note
What I'd like to do is grab this huge table from MSDN:
And Extract "Int32" when I know that I'm dealing with an "Integer" field, and extract "System.String" from SharePoint's note.
This kinda works, but it's the mother of all hacks:
var myTempItem = list.AddItem(new ListItemCreationInformation());
myTempItem.Update();
context.ExecuteQuery();
context.Load(myTempItem);
context.ExecuteQuery();
After creation, you can use:
myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName -> Gives-> System.Int32
Now, what's the proper way to do it? I'm just hoping the answer isn't a ten foot long switch case statement.
Since there is no SPField.FieldValueType property counterparts in SharePoint CSOM API, the following extension method demonstrates how to perform it:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.Client;
using Field = Microsoft.SharePoint.Client.Field;
namespace SharePoint.Client.Extensions
{
public static class FieldExtensions
{
public static Type GetFieldValueType(this Field field)
{
var table = new Dictionary<FieldType, Type>();
table[FieldType.Guid] = typeof(Guid);
table[FieldType.Attachments] = typeof(bool);
table[FieldType.Boolean] = typeof(bool);
table[FieldType.Choice] = typeof (string);
table[FieldType.CrossProjectLink] = typeof(bool);
table[FieldType.DateTime] = typeof(DateTime);
table[FieldType.Lookup] = typeof(FieldLookupValue);
table[FieldType.ModStat] = typeof(int);
table[FieldType.MultiChoice] = typeof(string[]);
table[FieldType.Number] = typeof(double);
table[FieldType.Recurrence] = typeof(bool);
table[FieldType.Text] = typeof(string);
table[FieldType.URL] = typeof(FieldUrlValue);
table[FieldType.URL] = typeof(FieldUrlValue);
table[FieldType.User] = typeof(FieldUserValue);
table[FieldType.WorkflowStatus] = typeof(int);
table[FieldType.ContentTypeId] = typeof(ContentTypeId);
table[FieldType.Note] = typeof(string);
table[FieldType.Counter] = typeof(int);
table[FieldType.Computed] = typeof(string);
table[FieldType.Integer] = typeof(int);
table[FieldType.File] = typeof(string);
if (!table.ContainsKey(field.FieldTypeKind))
throw new NotSupportedException(string.Format("Unknown field type: {0}", field.FieldTypeKind));
return table[field.FieldTypeKind];
}
}
}
Usage
var list = ctx.Web.Lists.GetByTitle(listTitle);
var fields = list.Fields;
ctx.Load(fields);
ctx.ExecuteQuery();
foreach (var field in fields)
{
if (field.FieldTypeKind != FieldType.Invalid)
{
var fieldValueType = field.GetFieldValueType();
Console.WriteLine("{0} : {1}", field.InternalName, fieldValueType);
}
}
Expanding on #Vadim's answer, here is a version that does not construct a new dictionary every time the extension method is called;
namespace SharePoint.Client.Extensions
{
public static class FieldExtensions
{
private static Dictionary<FieldType, Type> _fieldTypes = new Dictionary<FieldType, Type>()
{
{ FieldType.Guid, typeof(Guid) },
{ FieldType.Attachments, typeof(bool)},
{FieldType.Boolean, typeof(bool)},
{FieldType.Choice, typeof(string)},
{FieldType.CrossProjectLink, typeof(bool)},
{FieldType.DateTime, typeof(DateTime)},
{FieldType.Lookup, typeof(FieldLookupValue)},
{FieldType.ModStat, typeof(int)},
{FieldType.MultiChoice, typeof(string[])},
{FieldType.Number, typeof(double)},
{FieldType.Recurrence, typeof(bool)},
{FieldType.Text, typeof(string)},
{FieldType.URL, typeof(FieldUrlValue)},
{FieldType.User, typeof(FieldUserValue)},
{FieldType.WorkflowStatus, typeof(int)},
{FieldType.ContentTypeId, typeof(ContentTypeId)},
{FieldType.Note, typeof(string)},
{FieldType.Counter, typeof(int)},
{FieldType.Computed, typeof(string)},
{FieldType.Integer, typeof(int)},
{FieldType.File, typeof(string)}
};
public static Type GetFieldValueType(this Field field)
{
if (!_fieldTypes.ContainsKey(field.FieldTypeKind))
throw new NotSupportedException(string.Format("Unknown field type: {0}", field.FieldTypeKind));
return _fieldTypes[field.FieldTypeKind];
}
}
}
Generally, you need to do the mapping you describe, not the myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName
method.
The reason is, myTempItemCreated[fieldImTryingToGetTypeOf.Title].GetType().FullName
will fail in the scenario where the field value (e.g. Title) is null for that particular ListItem object. (While Title is not generally null, other fields can be and are null). And obviously null does not give you an object on which you can call the GetType() method (you would get a NullReferenceException obviously).
So, for a general solution to the problem, one does indeed have to map the String returned from TypeAsString of the list field, calling from the list object / list field, and not from the list item.
You can get the type of field using the following snippet:
item.Fields["Title"].FieldValueType.FullName
Good morning.
I have the need to create a little helper class, using generics, however my knowledge of generics is very low.
So here is what i need.
i have defined enums in C#, to have description properties. for example
public enum EnumLineItemErrorCode
{
[Description("None")]
None = 0,
[Description("helpful Desc")]
MissnigA= 1,
[Description("another desc")]
MissingB = 2
}
I also have created helper functions that get he description out of the enums like
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
Now if i need the desc i can call the method like the following
EnumerationsHelper.GetEnumDescription(EnumLineItemErrorCode.MissnigA);
However when we want to do bindinds to a datasource i currently do the following for each value on the enum.
dropdownList.Add(new ListItem(EnumerationsHelper.GetEnumDescription(EnumLineItemErrorCode.MissnigA), EnumLineItemErrorCode.MissnigA.ToString()));
But this approach is inflexible as the enum grows in size and also because it does not automatically adds a value to the list if i just add it to the enum.
So my question is.
Can u create a helper method that will return me a list of Description, Value,
where description is the enum description and the value is the enum internal value.
For example on the code that i have will be used as
object t EnumerationsHelper.GetDescriptionAndValuesAslist(EnumLineItemErrorCode);
and object t is structure of with values <"None",None">,<"MissnigA","helpful Desc">,<"MissingB","another desc">
Thank you in advance.
This will do the job.
private string[] GetEnumDescriptions<T>()
{
return Enum.GetValues(typeof(T))
.Cast<Enum>()
.Select(GetEnumDescription)
.ToArray();
}
Use case:
var descriptions = GetDescriptions<EnumLineItemErrorCode>();
Note: If T is not of type Enum Enum.GetValues method will throw exception.
I quickly wrote the following helper method for you
public static IDictionary<string, string> GetEnumBindings<T>()
{
if (!typeof(Enum).IsAssignableFrom(typeof(T)))
{
throw new ArgumentException("The provided type is not an enum");
}
var result = new Dictionary<string, string>();
var fieldNames = Enum.GetNames(typeof (T));
foreach (var fieldName in fieldNames)
{
var fieldAttributes = typeof (T).GetField(fieldName)
.GetCustomAttributes(typeof (DescriptionAttribute), false);
var description = fieldAttributes.Any()
? ((DescriptionAttribute) fieldAttributes.First()).Description
: fieldName;
result.Add(fieldName, description);
}
return result;
}
And use case is:
var bindings = GetEnumBindings<EnumLineItemErrorCode>();
var listItems = bindings.Select(b => new ListItem(b.Value, b.Key));
After that just add listItems to your DropDownList.
Good luck!
UPD: Since description is not unique, I modified code a little bit to add field name as a key to the dictionary.
This is what you are looking for: Enum.GetValues
I'm trying to call a function in a dynamic linq select statement, but im getting error:
No property or field 'A' exists in type 'Tuple2'
Example code:
void Main()
{
var a = new Tuple<int, int>(1,1);
var b = new[]{ a };
var q = b.AsQueryable().Select("A.Test(it.Item1)");
q.Dump();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
How should I change my code to get this working?
If I call built in function Convert.ToInt32 for example it works fine.
var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");
Also how do I cast a property using dynamic linq?
var q = b.AsQueryable().Select("((float)it.Item1)");
I'll say that the dynamic-linq isn't "strong enough" to do these things. It looks for methods only in the given objects and some special classes: Math, Convert, the various base types (int, float, string, ...), Guid, Timespan, DateTime
The list of these types is clearly visible if you use ilspy/reflector on the file. They are in System.Linq.Dynamic.ExpressionParser.predefinedTypes .
Now, clearly I could be wrong, but this works: .Select("Guid.NewGuid().ToString()").Cast<string>().ToArray()
showing that it's quite probable that that is the "correct" list.
There is an article here on how to modify Dynamic LINQ if you are interested http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html
Now, an intelligent man would take the source of dynamic linq and simply expand that array... But here there aren't intelligent men... There are only programmers that want blood! Blood but especially innards!
var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");
FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);
Type[] predefinedTypes = (Type[])field.GetValue(null);
Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type
field.SetValue(null, predefinedTypes);
Do this (with the types you want) BEFORE the first call to Dynamic Linq (because after the first call the methods/properties of these types are cached)
Explanation: we use reflection to add our object(s) to this "special list".
I know there is already an accepted answer on this but it did not work for me. I am using Dynamic Linq 1.1.4. I wanted to do a query like this
$.GetNewestRisk() == null
Where GetNewestRisk() is a public method on the object represented by $. I kept getting this error "Error running query, Methods on type 'Patient' are not accessible (at index 2)".
I found in the source code there is a GlobalConfig object that allows a custom provider to be assigned which will hold all of the types you may want to work with. Here is the source code for the custom provider:
public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
public HashSet<Type> GetCustomTypes()
{
HashSet<Type> types = new HashSet<Type>();
types.Add(typeof(Patient));
types.Add(typeof(RiskFactorResult));
types.Add(typeof(PatientLabResult));
types.Add(typeof(PatientVital));
return types;
}
}
Here is how I am using it:
System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();
After making this call I am able to call methods on the objects inside of the expression.
#xanatos answer doesn't work for .Net Core version. So I've found something similar related by #Kent on the System.Dynamic.Linq.Core tests DynamicExpressionParserTests written by the library's author himself.
The given TestCustomTypeProviderClass allows you to use the DynamicLinqType class annotation which is pretty usefull for this problem.
To answer to question, you then just needed to defined the class (ensure to annotate with DynamicLinqType) :
[DynamicLinqType]
public static class A
{
public static int Test(int i)
{
return i++;
}
}
Add a customTypeProvider as mentioned above :
private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
private HashSet<Type> _customTypes;
public virtual HashSet<Type> GetCustomTypes()
{
if (_customTypes != null)
{
return _customTypes;
}
_customTypes = new HashSet<Type>(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly }));
return _customTypes;
}
}
and use a ParsingConfig with the configurable Select to call it :
var config = new ParsingConfig
{
CustomTypeProvider = new TestCustomTypeProvider()
};
var q = b.AsQueryable().Select(config, "A.Test(it.Item1)");
#Armand has put together a brilliant solution for this issue, and being the only solution I was able to find regarding this I want to add to it for anyone who tries the same approach.
The class that is marked with...
[DynamicLinqType]
... must be taken into consideration when you run the following line:
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })
In the solution provided above, this assumes the class that contains the function to be evaluated is on the same class the code currently resides in. If the methods are to be used outside of said class, the assembly will need to change.
FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { typeof(AnotherClassName).Assembly })
Nothing changes from the solution above, this is just for clarification for anyone attempting to use it.
As regards the current version (1.2.19) of Dynamic LINQ, you will probably get another exception:
System.Linq.Dynamic.Core.Exceptions.ParseException : Enum value 'Test' is not defined in enum type 'A'
To make DLINQ know your type 'A', you have two options:
Set up parsing config with your own custom types provider where you directly specify the type 'A'.
Mark your type with the attribute [DynamicLinqType]. If that type is loaded into the current domain (that's the usual case), you don't have to do anything more since the default custom type provider already scans the current AppDomain for types marked with [DynamicLinqType]. And only if that's not the case, i.e. your type is not loaded into the current domain, you have to do something like in that answer.
What if you would like to use both approaches - the first for type 'A' and the second for type 'B'? In that case, you just have to "merge" your type 'A' with the default provider types:
public class DynamicLinqTests
{
[Test]
public void Test()
{
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var parsingConfig = new ParsingConfig
{
ResolveTypesBySimpleName = true,
CustomTypeProvider = new TestCustomTypesProvider()
};
var queryWithA = b.AsQueryable().Select(parsingConfig, "A.Test(it.Item1)");
queryWithA.ToDynamicList();
var queryWithB = b.AsQueryable().Select(parsingConfig, "B.Test(it.Item1)");
queryWithB.ToDynamicList();
}
public static class A
{
public static int Test(int i)
{
return i++;
}
}
[DynamicLinqType]
public static class B
{
public static int Test(int i)
{
return i++;
}
}
public class TestCustomTypesProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
var customTypes = base.GetCustomTypes();
customTypes.Add(typeof(A));
return customTypes;
}
}
}
I may be confused but your syntax whereby you are using a string in your Selects doesn't compile for me. The following syntax works:
var q = b.AsQueryable().Select(it => A.Test(it.Item1));
var b = new[]{ a };
The above array is don't know what type of array , and it's not type safe ?
Your values are assigned in variant data type so it's not integer value (I think string value) ,when you get this values in your query must need to convert.toint32() because your class parameter data type is integer
Please try it
var b = new **int**[]{ a };
instead of var b = new[]{ a };
The important hint is here (in bold):
No property or field 'xxx' exists in **type** 'xxx'
And Please look this for previous discussion :
Dynamic Linq - no property or field exists in type 'datarow'
The following works for me:
var a = new Tuple<int, int>(1, 1);
var b = new[] { a };
var q = b.AsQueryable().Select(it=>A.Test(it.Item1));
var q1 = b.AsQueryable().Select(it => Convert.ToInt32(it.Item1));
var q2 = b.AsQueryable().Select(it => (float) it.Item1);