Can a PSObject have properties that are PSObjects? - c#

I have a C# Cmdlet that interacts with a REST service, deserializing JSON as dynamic objects (ExpandoObject). When an expando is passed to WriteObject it gets written to the pipeline as a dictionary so I am converting it to a PSObject like so in order to make its properties accessible by the likes of Select-Object:
protected void WriteDynamicObject(dynamic o)
{
var psObject = ConvertToPSObject(o);
WriteObject(psObject);
}
private static PSObject ConvertToPSObject(dynamic o)
{
var d = o as IDictionary<string, object>;
Debug.Assert(d != null);
var record = new PSObject();
foreach (var kvp in d)
{
// if the value is itself an expando, convert it recursively
var value = kvp.Value is ExpandoObject ? ConvertToPSObject(kvp.Value) : kvp.Value;
record.Properties.Add(new PSNoteProperty(kvp.Key, value));
}
return record;
}
This works swimmingly for all of the dynamic object's properties except for properties that are themselves a complex type. The above code will recursively convert non-scalar child objects into PSObject instances. As an example the projectTeam property of this JSON result is converted to a PSOBject:
{
"kind": "storage#bucketAccessControl",
"role": "OWNER",
"projectTeam": {
"projectNumber": "930617506804",
"team": "owners"
},
"etag": "CAE="
}
When I try to select the projectTeam property from the pipeline I get a string representation and cannot select the properties of that sub object:
projectTeam
-----------
#{projectNumber=930617506804; team=owners}
#{projectNumber=930617506804; team=editors}
#{projectNumber=930617506804; team=viewers}
I guess I was expecting to be able to select or otherwise navigate the object hierarchy. Are PSObjects intended to be essentially a flat set of name value pairs or can they represent an object hierarchy? If so, how does one represent a nested object such that its properties are selectable or is it necessary to flatten the object hierarchy?

Related

How to dynamically override all properties of dynamic object?

I have two dynamic objects and I'd want to overwrite values of object A with values of object B
The problem is that both are of type dynamic
I managed to extract names of the properties with
TypeDescriptor.GetProperties(oldObject);
but I'm not sure how can I read & set dynamic property of dynamic object
something like oldObject.GetType().GetProperty("Test") won't work since even GetType().GetProperties() return empty collection
Thanks in advance
Below way you can get all the properties and their values.
dynamic dynamicObj = //define it
object obj = dynamicObj;
string[] propertyNames = obj.GetType().GetProperties().Select(p => p.Name).ToArray();
foreach (var name in propertyNames)
{
object value = obj.GetType().GetProperty(name).GetValue(obj, null);
}
Use this for private fields.
obj.GetType().GetProperties(BindingFlags.NonPublic);

Get properties from dynamic object using startswith

I want to get all properties from json that StartsWith particular text
dynamic results = JsonConvert.DeserializeObject<dynamic>(json);
So now below is what i get in results
{"abc" : "Text", "abcde" : "Text2","prop" : "myprop"}
Is it possible to do something like
results.Where(x => x.StartsWith("abc"))
You could simply use results.GetType().GetProperties(), which will give you an array of properties present in the deserialized JSON object.
You could then iterate over that array to get the PropertyInfo objects whose Name starts with whatever string you want, and call GetValue() to obtain the properties' values of interest.
Or you simply don't deserialize at all, but parse the object and treat it as JSON:
var jObject = JObject.Parse(jsonString);
foreach (var rootProperty in jObject)
{
if (rootProperty.Key.StartsWith("whatever"))
{
var valueOfInterest = rootProperty.Value;
}
}
Simply retrieve the runtime-type of the result-object and query its properties using Type.GetProperties:
var type = results.GetType();
type.GetProperties().Where(x => x.Name.StartsWith("abc"));
EDIT: Because any method called on an instance of dynamic is dynamic as well, you have to cast the result of results.GetType into Type. Otherwise you´ll get a compiler-err stating that you can´t use an anonymous method on a dynamically bound operation.
var type = (Type)results.GetType();

Deserialize JSON string into class with reflection

I'm trying to deserialize a JSON string into a custom class. I have to use reflection. I have a dictionary that I serialize, send over to an HttpPut method, deserialize the JSON string, and read the dictionary fields. Here's what I have so far:
I'm putting values into the Dictionary like this:
Dictionary<string, object> valuesToUpdate = new Dictionary<string, object>();
Person p = new Person();
p.personName = "OrigName";
p.age = "25";
p.isAlive = true;
valuesToUpdate.Add("Person", p);
valuesToUpdate.Add("Length", 64.0);
I'm using JSON to serialize it like this:
string jsonString = JsonConvert.SerializeObject(valuesToUpdate);
I then take the jsonString and send it over to a REST API PUT method. The PUT method updates various variables on a custom object based on the Key values in the dictionary using reflection (In this example I'm updating customObject.Person and customObject.Length).
The PUT call deserializes the jsonString like this:
Dictionary<string, object> newFields = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString);
I iterate through newFields and want to use reflection to update customObject's "Person" class. This is my HttpPut method that reads the jsonString:
[HttpPut("/test/stuff")]
public string PutContact([FromBody]dynamic jsonString)
{
Dictionary<string, object> newFields = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonString);
foreach (var field in newFields)
{
Console.WriteLine("\nField key: " + field.Key);
Console.WriteLine("Field value: " + field.Value + "\n");
PropertyInfo propInfo = typeof(Contact).GetProperty(field.Key);
Type propertyType = propInfo.PropertyType;
var value = propInfo.GetValue(contactToUpdate, null);
if (propertyType.IsClass)
{
propInfo.SetValue(contactToUpdate, field.Value, null);
}
}
}
This generates the error:
Object of type Newtonsoft.Json.Linq.JObject' cannot be converted to type 'Person';
I've also tried using JSON's PopulateObject method but it returned this error:
Newtonsoft.Json.JsonSerializationException: Cannot populate JSON object onto type 'Person'. Path 'personName', line 1....
So basically, how can you go about taking a JSON string, converting it to a class (in my case the 'Person' class), and setting it to customObject's Person field with reflection?
if (propertyType.IsClass)
{
propInfo.SetValue(contactToUpdate, ((JObject)field.Value).ToObject(propertyType), null);
}

Cannot access properties after JSON deserialization into dynamic

I'm having some issues accessing dynamic properties after JSON deserialization. Here is the JSON:
{
"user" : {
"511221" :{
"timestamp" : 1403365646384,
"version" : -81317051,
"email" : "user#name.com",
"name" : "My Name",
"structures" : [ "structure.62dd1ff1-f96b-22e3-8d4e-22000c20725r" ]
}
},
}
There's actually two problems here. First is that "511221" changes for each user. This is given when authenticating. I can't create a user class and then create another class which name keeps changing.
Also, it's a number. AFAIK, there is no way to have a class name as a number. What are my options here? I cannot control what gets returned by the API.
So instead of deserialization into a predefined class, I have a dynamic, like this:
dynamic jsonObject = JsonConvert.DeserializeObject(response);
I want to be able to do this:
string[] structures = jsonObject.user.(authInfo.userid).structures;
In words, I want (authInfo.userid) to input the user id as a string above, however this does not seem possible. I have also tried reflection:
private static object ReflectOnPath(object o, string path)
{
object value = o;
string[] pathComponents = path.Split('.');
foreach (var component in pathComponents)
{
Type type = value.GetType();
System.Reflection.PropertyInfo pi = type.GetProperty(component);
//pi is null here, I have no idea why
value = pi.GetValue(value);
}
return value;
}
So it could be called like this to do the same as shown above:
string[] structures = ReflectOnPath(jsonObject, "user." + authInfo.userid + ".structures") as string[];
However, when this is called, GetProperty() on type (JObject) is null. I know the property "user" exists, but why can't I access it?
You can deserialize your JSON to JObject instead of dynamic, then you can access the property by property name dynamically, for example :
JObject jObject = JsonConvert.DeserializeObject<JObject>(response);
var user = "511221";
var structures = jObject["user"][user]["structures"][0];
//given JSON input as in this question,
//following line will print "structure.62dd1ff1-f96b-22e3-8d4e-22000c20725r"
Console.WriteLine(structures);

Getting values out of the JSON object in C#

I have a JSON structure like below.
{"name":"user1","param":{"showid":"test"}}
I will pass the value of JSON structure to a program where the values of the JSON object will be fetch out.
But the key values will differ for each JSON object.
SO i couldnt create a structure for the JSON object to retrieve the values.
ie : The next time the JSON object may be like below.
{"name1":"user2","param1":{"showname":"test1"}}
How to iterate the key value pair from the JSON structure in c#?
You can use System.Web.Script.Serialization.JavaScriptSerializer (System.Web.Extensions.dll) and load it into a datatype "dynamic", then you can access the properties like a dictionary.
Or you can use reflection to find the available properties/fields, and get a field's/property's value.
public static Dictionary<string, object> ToPropertyDictionary(this object obj)
{
var dictionary = new Dictionary<string, object>();
foreach (var propertyInfo in obj.GetType().GetProperties())
if (propertyInfo.CanRead && propertyInfo.GetIndexParameters().Length == 0)
dictionary[propertyInfo.Name] = propertyInfo.GetValue(obj, null);
return dictionary;
}

Categories