InvalidCastException JToken to <Class> JSON.net - c#

TL;DR: I was trying to create a class which would hold nested JSON data.
I eventually solved my own problem, but #dbc was very helpful and they have a solution which may be slightly faster if you want to implement it their way. I have fully documented my solution, with example usage, and marked it as answered below.
I'm creating a project in which I intend to store lots of nested JSON data.
Instead of creating a hundred classes, each with their own variables/attributes, and then having to modify them every time I want to change something, I'd like to create a simple "dynamic object".
This object holds the root of all data, as well as all the children's data. In JSON, this is represented by:
{
"name":"foo",
"id":0,
"attributes":
{
"slippery":true,
"dangerous":true
},
"costs":
{
"move":1,
"place":2,
"destroy":3
}
}
where the root structure holds the data "name" and "id", as well as children "attributes" and "costs" each containing their own data.
I'm using the json.net library for this, and my current class looks like this:
public class Data : JObject
{
public void CreateChildUnderParent(string parent, string child)
{
Data obj = GetValueOfKey<Data>(parent);
if(obj != null)
obj.CreateChild(child);
}
public void CreateChild(string child)
{
AddKey(child, new Data());
}
public void AddKeyToParent(string parent, string key, JToken value)
{
Data parentObject = GetValueOfKey<Data>(parent);
if(parentObject != null)
parentObject.AddKey(key, value);
}
public void AddKey(string key, JToken value)
{
Add(key, value);
}
public void RemoveKeyFromParent(string parent, string key)
{
Data parentObject = GetValueOfKey<Data>(parent);
if(parentObject != null)
parentObject.RemoveKey(key);
}
public void RemoveKey(string key)
{
Remove(key);
}
public T GetValueFromParent<T>(string parent, string key)
{
Data parentObject = GetValueOfKey<Data>(parent);
if(parentObject != null)
return parentObject.GetValue(key).ToObject<T>();
return default;
}
public T GetValueOfKey<T>(string key)
{
foreach (var kvp in this)
if (kvp.Value is Data)
{
T value = ((Data)kvp.Value).GetValueOfKey<T>(key);
if (value != null)
return value;
}
JToken token = GetValue(key);
if(token != null)
return token.ToObject<T>(); //throws exception
return default;
}
}
I can add children just fine, but my issue comes when I try to access them. An InvalidCastException is thrown within my
public T GetValueOfKey<T>(string key)
method whenever I call it using
Data
as the generic type.
For example:
Data data = GetValueOfKey<Data>("attributes");
throws an exception. I'm not sure why this is happening, so any help would be greatly appreciated!
EDIT:
Here is the complete error log thrown:
InvalidCastException: Specified cast is not valid.
(wrapper castclass) System.Object.__castclass_with_cache(object,intptr,intptr)
Newtonsoft.Json.Linq.JToken.ToObject[T] () (at <97722d3abc9f4cf69f9e21e6770081b3>:0)
Data.GetValueOfKey[T] (System.String key) (at Assets/Scripts/Attributes/Object/Data.cs:74)
Data.AddKeyToParent (System.String parent, System.String key, Newtonsoft.Json.Linq.JToken value) (at Assets/Scripts/Attributes/Object/Data.cs:23)
DataController.Awake () (at Assets/Scripts/Controllers/DataController.cs:35)
and an example of instantiation which causes this exception:
public class DataController
{
void Awake()
{
Data data = new Data();
data.AddKey("name", "foo");
data.CreateChild("attributes");
data.AddKeyToParent("attributes", "slippery", true); //throws exception (line 35)
}
}
UPDATE (10/20/18):
Ok so I went through my code this afternoon and rewrote it as a wrapper class, now the root JObject is stored within a variable in my Data, and accessor methods adjust its properties.
However, I ran into a problem. Here's the updated class (minified to the problem):
public class Data
{
public JObject data;
public Data()
{
data = new JObject();
}
public void AddChild(string child)
{
data.Add(child, new JObject());
}
public void AddKeyWithValueToParent(string parent, string key, JToken value)
{
JObject parentObject = GetValueOfKey<JObject>(parent);
if(parentObject != null)
parentObject.Add(key, value);
}
public void AddKeyWithValue(string key, JToken value)
{
data.Add(key, value);
}
public T GetValueOfKey<T>(string key)
{
return GetValueOfKey<T>(key, data);
}
private T GetValueOfKey<T>(string key, JObject index)
{
foreach (var kvp in index)
if (kvp.Value is JObject)
{
T value = GetValueOfKey<T>(key, kvp.Value.ToObject<JObject>());
if (value != null)
return value;
}
JToken token = index.GetValue(key);
if (token != null)
return token.ToObject<T>();
return default;
}
}
And here is an example of how to construct a Data object, and use its methods:
public class DataController
{
void Awake() {
Data data = new Data();
data.AddKeyWithValue("name", "foo");
data.AddChild("attributes");
data.AddKeyWithValueToParent("attributes", "slippery", true);
}
}
So everything in terms of adding key-value pairs, and creating children works wonderfully! No InvalidCastException at all, yay! However, when I try to serialize the object through JsonConvert.SerializeObject(data), it doesn't fully serialize it.
I have the program output to the console to show the serialization, and it looks like this:
{"data":{"name":"foo","attributes":{}}}
I've already checked to make sure that when I call data.AddKeyWithValueToParent("attributes", "slippery", true), it does indeed find the JObject value with the key attributes and even appears to successfully add the new key-value pair "slippery":true under it. But for some reason, serializing the root object data does not seem to identify that anything lies within the attributes object. Thoughts?
What I think may be happening, is that the value returned from GetValueOfKey is not acting as a reference object, but rather an entirely new object, so changes to that are not reflected within the original object.

I figured it out! I was right, the value returned from my GetValueOfKey method was returning a completely new object, and not a reference to the instance it found. Looking through my code, that should have been immediately obvious, but I'm tired and I was hoping for everything to be easy haha.
Anyway, for anyone who ever has the same question, and is just looking for a simple way to store and read some nested key-value pairs using the Json.NET library, here is the finished class that will do that (also serializable, and deserializable using JsonConvert):
public class Data
{
[JsonProperty]
private JObject data;
public Data()
{
data = new JObject();
}
public void AddChildUnderParent(string parent, string child)
{
JObject parentObject = GetValueOfKey<JObject>(parent);
if (parentObject != null)
{
parentObject.Add(child, new JObject());
ReplaceObject(parent, parentObject);
}
}
public void AddChild(string child)
{
data.Add(child, new JObject());
}
public void AddKeyWithValueToParent(string parent, string key, JToken value)
{
JObject parentObject = GetValueOfKey<JObject>(parent);
if(parentObject != null)
{
parentObject.Add(key, value);
ReplaceObject(parent, parentObject);
}
}
public void AddKeyWithValue(string key, JToken value)
{
data.Add(key, value);
}
public void RemoveKeyFromParent(string parent, string key)
{
JObject parentObject = GetValueOfKey<JObject>(parent);
if (parentObject != null)
{
parentObject.Remove(key);
ReplaceObject(parent, parentObject);
}
}
public void RemoveKey(string key)
{
data.Remove(key);
}
public T GetValueFromParent<T>(string parent, string key)
{
JObject parentObject = GetValueOfKey<JObject>(parent);
if (parentObject != null)
return parentObject.GetValue(key).ToObject<T>();
return default;
}
public T GetValueOfKey<T>(string key)
{
return GetValueOfKey<T>(key, data);
}
private T GetValueOfKey<T>(string key, JObject index)
{
foreach (var kvp in index)
if (kvp.Value is JObject)
{
T value = GetValueOfKey<T>(key, (JObject)kvp.Value);
if (value != null)
return value;
}
JToken token = index.GetValue(key);
if (token != null)
{
data = token.Root.ToObject<JObject>();
return token.ToObject<T>();
}
return default;
}
public void ReplaceObject(string key, JObject replacement)
{
ReplaceObject(key, data, replacement);
}
private void ReplaceObject(string key, JObject index, JObject replacement)
{
foreach (var kvp in index)
if (kvp.Value is JObject)
ReplaceObject(key, (JObject)kvp.Value, replacement);
JToken token = index.GetValue(key);
if (token != null)
{
JToken root = token.Root;
token.Replace(replacement);
data = (JObject)root;
}
}
}
That should get anyone a good head start. I plan on updating my code with params modifiers in some places to allow for multiple calls, but for now I'm just happy that I got it working. You'll notice that I had to create a ReplaceObject method, because without it, the original private JObject data was never actually updated to account for the changes made to the variable returned from GetValueOfKey.
Anyway, a big thanks to #dbc for all their help during this whole thing, and I hope this post helps someone in the future!
-ShermanZero
EDIT:
So I spent a little more time developing the class, and I think I have it pinned down to a universal point where anyone could simply copy-paste and easily implement it into their own program. Although, I personally think that #dbc has a faster solution if you care about nanosecond-millisecond differences in speed. For my own personal use though, I don't think it will make much of a difference.
Here is my full implementation, complete with documentation and error logging:
public class Data
{
[JsonExtensionData]
private JObject root;
private Texture2D texture;
private char delimiter = ',';
/// <summary>
/// Creates a new Data class with the default delimiter.
/// </summary>
public Data()
{
root = new JObject();
}
/// <summary>
/// Creates a new Data class with a specified delimiter.
/// </summary>
/// <param name="delimiter"></param>
public Data(char delimiter) : this()
{
this.delimiter = delimiter;
}
/// <summary>
/// Adds a child node to the specified parent(s) structure, which is split by the delimiter, with the specified name.
/// </summary>
/// <param name="name"></param>
/// <param name="parents"></param>
public void AddChild(string name, string parents)
{
AddChild(name, parents.Split(delimiter));
}
/// <summary>
/// Adds a child node to the specified parent(s) structure with the specified name.
/// </summary>
/// <param name="name"></param>
/// <param name="parents"></param>
public void AddChild(string name, params string[] parents)
{
string lastParent;
JObject parentObject = ReturnParentObject(out lastParent, parents);
if (parentObject != null)
{
parentObject.Add(name, new JObject());
ReplaceObject(lastParent, parentObject, parents);
} else
{
string message = "";
foreach (string parent in parents)
message += parent + " -> ";
throw new ParentNotFoundException($"The parent '{ message.Substring(0, message.LastIndexOf("->")) }' was not found.");
}
}
/// <summary>
/// Adds a child node to the root structure with the specified name.
/// </summary>
/// <param name="name"></param>
public void AddChild(string name)
{
root.Add(name, new JObject());
}
/// <summary>
/// Adds the specified key-value pair to the specified parent(s) structure, which is split by the delimiter.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="parents"></param>
public void AddKeyWithValue(string key, JToken value, string parents)
{
AddKeyWithValue(key, value, parents.Split(delimiter));
}
/// <summary>
/// Adds the specified key-value pair to the specified parent(s) structure.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="parents"></param>
public void AddKeyWithValue(string key, JToken value, params string[] parents)
{
string lastParent;
JObject parentObject = ReturnParentObject(out lastParent, parents);
if (parentObject != null)
{
parentObject.Add(key, value);
ReplaceObject(lastParent, parentObject, parents);
} else
{
string message = "";
foreach (string parent in parents)
message += parent + " -> ";
throw new ParentNotFoundException($"The parent '{ message.Substring(0, message.LastIndexOf("->")) }' was not found.");
}
}
/// <summary>
/// Adds the specified key-value pair to the root structure.
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void AddKeyWithValue(string key, JToken value)
{
root.Add(key, value);
}
/// <summary>
/// Removes the specified key from the specified parent(s) structure, which is split by the delimiter.
/// </summary>
/// <param name="key"></param>
/// <param name="parents"></param>
public void RemoveKey(string key, string parents)
{
RemoveKey(key, parents.Split(delimiter));
}
/// <summary>
/// Removes the specified key from the specified parent(s) structure.
/// </summary>
/// <param name="key"></param>
/// <param name="parents"></param>
public void RemoveKey(string key, params string[] parents)
{
string lastParent;
JObject parentObject = ReturnParentObject(out lastParent, parents);
if (parentObject != null)
{
parentObject.Remove(key);
ReplaceObject(lastParent, parentObject, parents);
} else
{
string message = "";
foreach (string parent in parents)
message += parent + " -> ";
throw new ParentNotFoundException($"The parent '{ message.Substring(0, message.LastIndexOf("->")) }' was not found.");
}
}
/// <summary>
/// Removes the specified key from the root structure.
/// </summary>
/// <param name="key"></param>
public void RemoveKey(string key)
{
root.Remove(key);
}
/// <summary>
/// Returns if the specified key is contained within the parent(s) structure, which is split by the delimiter.
/// </summary>
/// <param name="key"></param>
/// <param name="parents"></param>
/// <returns></returns>
public bool HasValue(string key, string parents)
{
return HasValue(key, parents.Split(delimiter));
}
/// <summary>
/// Returns if the specified key is contained within the parent(s) structure.
/// </summary>
/// <param name="key"></param>
/// <param name="parents"></param>
/// <returns></returns>
public bool HasValue(string key, params string[] parents)
{
//string lastParent = parents[parents.Length - 1];
//Array.Resize(ref parents, parents.Length - 1);
string lastParent;
JObject parentObject = ReturnParentObject(out lastParent, parents);
if (parentObject == null)
return false;
else if (parentObject == root && parents.Length > 0)
return false;
IDictionary<string, JToken> dictionary = parentObject;
return dictionary.ContainsKey(key);
}
/// <summary>
/// Returns the deepest parent object referenced by the parent(s).
/// </summary>
/// <param name="lastParent"></param>
/// <param name="parents"></param>
/// <returns></returns>
private JObject ReturnParentObject(out string lastParent, string[] parents)
{
lastParent = null;
if(parents.Length > 0)
{
lastParent = parents[parents.Length - 1];
Array.Resize(ref parents, parents.Length - 1);
return GetValueOfKey<JObject>(lastParent, parents);
}
return root;
}
/// <summary>
/// Returns the value of the specified key from the specified parent(s) structure, which is split by the delimiter.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="parents"></param>
/// <returns></returns>
public T GetValueOfKey<T>(string key, string parents)
{
return GetValueOfKey<T>(key, parents.Split(delimiter));
}
/// <summary>
/// Returns the value of the specified key from the specified parent(s) structure.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="parents"></param>
/// <returns></returns>
public T GetValueOfKey<T>(string key, params string[] parents)
{
JObject parentObject = null;
for(int i = 0; i < parents.Length; i++)
parentObject = GetValueOfKey<JObject>(parents[i].Trim(), parentObject == null ? root : parentObject);
return GetValueOfKey<T>(key, parentObject == null ? root : parentObject);
}
/// <summary>
/// Returns the value of the specified key from the root structure.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public T GetValueOfKey<T>(string key)
{
return GetValueOfKey<T>(key, root);
}
/// <summary>
/// Returns the value of the specified key from a given index in the structure.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <param name="index"></param>
/// <returns></returns>
private T GetValueOfKey<T>(string key, JObject index)
{
JToken token = index.GetValue(key);
if (token != null)
return token.ToObject<T>();
foreach (var kvp in index)
if (kvp.Value is JObject)
{
T value = GetValueOfKey<T>(key, (JObject)kvp.Value);
if (value != null)
return value;
}
return default(T);
}
/// <summary>
/// Replaces an object specified by the given key and ensures object is replaced within the correct parent(s), which is split by the delimiter.
/// </summary>
/// <param name="key"></param>
/// <param name="replacement"></param>
/// <param name="parents"></param>
public void ReplaceObject(string key, JObject replacement, string parents)
{
ReplaceObject(key, root, replacement, parents.Split(delimiter));
}
/// <summary>
/// Replaces an object specified by the given key and ensures object is replaced within the correct parent(s).
/// </summary>
/// <param name="key"></param>
/// <param name="replacement"></param>
/// <param name="parents"></param>
public void ReplaceObject(string key, JObject replacement, params string[] parents)
{
ReplaceObject(key, root, replacement, parents);
}
/// <summary>
/// Replaces an object specified by the given key.
/// </summary>
/// <param name="key"></param>
/// <param name="replacement"></param>
public void ReplaceObject(string key, JObject replacement)
{
ReplaceObject(key, root, replacement);
}
/// <summary>
/// Replaces an object specified by the given key within the structure and updates changes to the root node.
/// </summary>
/// <param name="key"></param>
/// <param name="index"></param>
/// <param name="replacement"></param>
private void ReplaceObject(string key, JObject index, JObject replacement)
{
foreach (var kvp in index)
if (kvp.Value is JObject)
ReplaceObject(key, (JObject)kvp.Value, replacement);
JToken token = index.GetValue(key);
if (token != null)
{
JToken root = token.Root;
token.Replace(replacement);
this.root = (JObject)root;
}
}
/// <summary>
/// Replaces an object specified by the given key within the structure, ensuring object is replaced within the correct parent, and updates changes to the root node.
/// </summary>
/// <param name="key"></param>
/// <param name="index"></param>
/// <param name="replacement"></param>
/// <param name="parents"></param>
private void ReplaceObject(string key, JObject index, JObject replacement, params string[] parents)
{
foreach (var kvp in index)
if (kvp.Value is JObject)
{
bool valid = false;
foreach (string str in parents)
if (str.Trim() == kvp.Key)
valid = true;
if(valid)
ReplaceObject(key, (JObject)kvp.Value, replacement);
}
JToken token = index.GetValue(key);
if (token != null)
{
JToken root = token.Root;
token.Replace(replacement);
this.root = (JObject)root;
}
}
/// <summary>
/// Returns the root structure as JSON.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return root.ToString();
}
/// <summary>
/// A ParentNotFoundException details that the supplied parent was not found within the structure.
/// </summary>
private class ParentNotFoundException : Exception
{
public ParentNotFoundException() { }
public ParentNotFoundException(string message) : base(message) { }
public ParentNotFoundException(string message, Exception inner) : base(message, inner) { }
}
}
Usage example:
Data data = new Data();
data.AddKeyWithValue("name", "foo");
data.AddChild("costs");
data.AddChild("attributes");
data.AddKeyWithValue("move", 1, "costs");
data.AddKeyWithValue("place", 2, "costs");
data.AddKeyWithValue("destroy", 3, "costs");
data.AddChild("movement", "costs");
data.AddKeyWithValue("slippery", false, "costs", "movement");
data.AddChild("movement", "attributes");
data.AddKeyWithValue("slippery", true, "attributes", "movement");
if(data.HasValue("move", "costs")) {
Debug.Log(data.GetValueOfKey<int>("move", "costs")
Debug.Log(data);
}
And its output:
1
{
"name": "foo",
"costs": {
"move": 1,
"place": 2,
"destroy": 3,
"movement": {
"slippery": false
}
},
"attributes": {
"movement": {
"slippery": true
}
}
}

Related

Newtonsoft JSON serialize a template

I am trying to serialize a class to a JSON string. First, my actual code:
// Note that this class is inside a PCL
public class CommunicationMessage {
public String Key { get; set; }
public String Value { get; set; }
public List<CommunicationMessage> Childs { get; set; }
}
This is a template which can be converted to xml which could look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myproject>
<communicationmessage>
<header>
<participantid>1</participantid>
<deviceid>54325</deviceid>
<devicetype>Smartphone Samsung 4500</devicetype>
<timestamp>3456453445</timestamp>
<location>343-5343-64353</location>
<networkid>32</networkid>
<messageid>4003</messageid>
</header>
<data>
</data>
</communicationmessage>
</myproject>
As you can see, the variable Key is an Xml-Element which is named e.g communicationmessage.
Now U want the template beeing converted to a JSON string, but for sure I get instead of a element communicationmessage the element "Key":"communicationmessage". Is there a way to get something for an element like "mymessage":"This is a test" where "mymessage" is the Key and "This is a test" the value?
Thanks for any help
Solution
I solved it with this code
public class CommunicationMessageJSONSerializer : JsonConverter {
/// <summary>
/// Used to
/// </summary>
/// <param name="objectType"></param>
/// <returns></returns>
public override bool CanConvert(Type objectType) {
return typeof(CommunicationMessage).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
/// <summary>
/// Deserializes the JSON string
/// </summary>
/// <param name="reader"></param>
/// <param name="objectType"></param>
/// <param name="existingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
// Load the JSON object from the reader
JObject jsonObject = JObject.Load(reader);
// Get the First Token
JToken token = jsonObject.Children().First();
// The deserialized message
CommunicationMessage msg = ReadJSON(token);
return msg;
}
/// <summary>
/// This is the base method when deserializing a JSON string
/// </summary>
/// <param name="token"></param>
/// <returns>The root CommunicationMessage</returns>
private CommunicationMessage ReadJSON(JToken token) {
CommunicationMessage root = new CommunicationMessage();
if (token is JProperty) {
if (token.First is JValue) {
root.Key = ((JProperty)token).Name;
root.Value = (string)((JProperty)token).Value;
} else {
root.Key = ((JProperty)token).Name;
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref root);
}
}
} else {
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref root);
}
}
return root;
}
/// <summary>
/// This is the recursive method when deserializing a JSON string
/// </summary>
/// <param name="token"></param>
/// <param name="root">The root of the coming messages</param>
private void ReadRecursive(JToken token, ref CommunicationMessage root) {
if (token is JProperty) {
CommunicationMessage msg = new CommunicationMessage();
if (token.First is JValue) {
msg.Key = ((JProperty)token).Name;
msg.Value = (string)((JProperty)token).Value;
} else {
msg.Key = ((JProperty)token).Name;
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref msg);
}
}
root.Childs.Add(msg);
} else {
foreach (JToken child in token.Children()) {
ReadRecursive(child, ref root);
}
}
}
/// <summary>
/// Serializes a CommuicationMessage to a JSON string
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var msg = value as CommunicationMessage;
WriteRecoursive(writer, msg, serializer);
}
/// <summary>
/// This is the recursive method for serializing
/// </summary>
/// <param name="writer"></param>
/// <param name="msg"></param>
/// <param name="serializer"></param>
private void WriteRecoursive(JsonWriter writer, CommunicationMessage msg, JsonSerializer serializer) {
writer.WriteStartObject();
writer.Formatting = Formatting.Indented;
writer.WritePropertyName(msg.Key);
if (msg.Childs.Count > 0) {
writer.WriteStartArray();
foreach (CommunicationMessage child in msg.Childs) {
WriteRecoursive(writer, child, serializer);
}
writer.WriteEndArray();
} else {
writer.WriteValue(msg.Value);
}
writer.WriteEndObject();
}
}
If you have any ideas to improve the code, let me know it. I will keep the solution updated.
Thanks everyone for your help
Other than using custom serialisation or implementing ISerializable, you can do the following. But please note that there is a drawback to this method whereby all properties need to be wrapped like this otherwise the the properties will not get serialised. This quick and dirty method is good for small classes but if you are creating more complicated classes, it will be better to implement ISerializable
public class CommunicationMessage : Dictionary<string, object>
{
//this "hack" exposes the "Child" as a List
public List<CommunicationMessage> Childs
{
get {
return (List<CommunicationMessage>)this["Childs"];
}
set
{
this["Childs"] = value;
}
}
public CommunicationMessage()
{
this["Childs"] = new List<CommunicationMessage>();
}
}
Usage:
var m = new CommunicationMessage();
m["mymessage"] = "This is a test";
And the output should look like
{
"Childs": [],
"mymessage": "This is a test"
}
The alternative ISerializable implementation:
public class CommunicationMessage:ISerializable
{
public String Key { get; set; }
public String Value { get; set; }
public List<CommunicationMessage> Childs { get; set; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue(Key, Value);
PropertyInfo[] pi=this.GetType().GetProperties();
foreach(var p in pi)
{
if (p.Name == "Key" || p.Name == "Value")
continue;
info.AddValue(p.Name, p.GetValue(this));
}
}
}

Achieve different SELECT and INSERT behavior on NHibernate IUserType

Is there a way to separate the Create (INSERT) behavior from the SELECT behavior.
Let's say I have a database with a column that will return a string looking like this
Predecessors = "1,3,4,5"
In my application I want to use this string like an int array by implementing an IUserType
public interface IIntArray
{
int[] Items { get; set; }
}
public class IntArray : IIntArray
{
public int[] Items { get; set; }
public IntArray(string item)
{
if(string.IsNullOrEmpty(item))
return;
Items = System.Array.ConvertAll<string, int>(item.Split(new[] {','}), int.Parse);
// for older .net versions use the code below
// Items = Array.ConvertAll<string, int>(item.ToString().Split(new[] { ',' }), delegate(string str) { return int.Parse(str); });
}
}
public class IntArrayType : IUserType
{
#region Implementation of IUserType
/// <summary>
/// Compare two instances of the class mapped by this type for persistent "equality"
/// ie. equality of persistent state
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public new bool Equals(object x, object y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
return x.GetType() == y.GetType();
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a resultset.
/// Implementors should handle possibility of null values.
/// </summary>
/// <param name="rs">a IDataReader</param>
/// <param name="names">column names</param>
/// <param name="owner">the containing entity</param>
/// <returns></returns>
/// <exception cref="HibernateException">HibernateException</exception>
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
var value = NHibernateUtil.String.NullSafeGet(rs, names[0]);
if (value == null || (string.IsNullOrEmpty(value.ToString())))
{
return null;
}
return new IntArray(value.ToString());
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// </summary>
/// <param name="cmd">a IDbCommand</param>
/// <param name="value">the object to write</param>
/// <param name="index">command parameter index</param>
/// <exception cref="HibernateException">HibernateException</exception>
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
if (value == null)
{
((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value;
}
else
{
var state = (IIntArray)value;
((IDataParameter)cmd.Parameters[index]).Value = state.GetType().Name;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns>a copy</returns>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// During merge, replace the existing (<paramref name="target" />) value in the entity
/// we are merging to with a new (<paramref name="original" />) value from the detached
/// entity we are merging. For immutable objects, or null values, it is safe to simply
/// return the first parameter. For mutable objects, it is safe to return a copy of the
/// first parameter. For objects with component values, it might make sense to
/// recursively replace component values.
/// </summary>
/// <param name="original">the value from the detached entity being merged</param>
/// <param name="target">the value in the managed entity</param>
/// <param name="owner">the managed entity</param>
/// <returns>the value to be merged</returns>
public object Replace(object original, object target, object owner)
{
return original;
}
/// <summary>
/// Reconstruct an object from the cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="owner">the owner of the cached object</param>
/// <returns>a reconstructed object from the cachable representation</returns>
public object Assemble(object cached, object owner)
{
return cached;
}
/// <summary>
/// Transform the object into its cacheable representation. At the very least this
/// method should perform a deep copy if the type is mutable. That may not be enough
/// for some implementations, however; for example, associations must be cached as
/// identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <returns>a cacheable representation of the object</returns>
public object Disassemble(object value)
{
return value;
}
/// <summary>
/// The SQL types for the columns mapped by this type.
/// </summary>
public SqlType[] SqlTypes { get { return new[] { NHibernateUtil.String.SqlType }; } }
/// <summary>
/// The type returned by <c>NullSafeGet()</c>
/// </summary>
public Type ReturnedType { get { return typeof(IntArray); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
#endregion
}
In my NHibernate class I mapped to property like this
public virtual IntArray Predecessors { get; set; }
And the hbm mapping
<property name="Predecessors" type="Example.IntArrayType, Example" />
The IntArray class does it's job when reading data but when trying to put something back this doesn't work. What I would like to do is to somehow force the IntArray property to render the values of IntArray.Items to a comma separated string
string magic = string.Join(",", Predecessors.Items);
Thanks
Something like this should do the trick
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
var ints = value as IntArray;
if(ints != null && ints.Items != null)
{
NHibernate.NHibernateUtil.StringClob.NullSafeSet(cmd, string.Join(", ", ints.Items), index);
}
}

Mapping System.Drawing.Color

If I don't map Color but map an object that has a Color attribute, FluentNHibernate successfully maps it to a varbinary(max). However, that's extremely inefficient given that realistically Color is just composed of 4 bytes and I am keen on improving it without using a new type to Proxy it.
Internally within Color it is made up of four properties,
long value (cast down to int to represent ARGB)
short state, indicates if this is a known & valid colour
string name, the name of the colour if known.
short knownColor a value to indicate which known colour it is
So I have attempted to map this as follows.
public ColorMapping()
{
CompositeId()
.KeyProperty(c => Reveal.Member<Color, long>("value"))
.KeyProperty(c => Reveal.Member<Color, short>("state"))
.KeyProperty(c => Reveal.Member<Color, string>("name"))
.KeyProperty(c => Reveal.Member<Color, short>("knownColor"));
}
However, on usage I get the following exception,
Class Initialization method DataContextTest.ClassInitialise threw
exception. FluentNHibernate.Cfg.FluentConfigurationException:
FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> FluentNHibernate.Cfg.FluentConfigurationException: An invalid or
incomplete configuration was used while creating a SessionFactory.
Check PotentialReasons collection, and InnerException for more detail.
---> NHibernate.MappingException: Could not compile the mapping
document: (XmlDocument) ---> NHibernate.MappingException: Could not
determine type for:
System.Linq.Expressions.Expression1[[System.Func2[[System.Drawing.Color,
System.Drawing, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a],[System.Int64, mscorlib,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],
mscorlib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], System.Core, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089, for columns:
NHibernate.Mapping.Column(Member).
Am I misusing Reveal? If so, how am I meant to use it?
i would map it as UserType which converts it back and forth
// in mapping
Map(x => x.Color).Column("ColorARGB").CustomType<ColorUserType>();
[Serializable]
class ColorUserType : IUserType
{
public object Assemble(object cached, object owner)
{
return DeepCopy(cached);
}
public object DeepCopy(object value)
{
return value;
}
public object Disassemble(object value)
{
return DeepCopy(value);
}
bool IUserType.Equals(object x, object y)
{
var colorX = x as Color;
var colorY = y as Color;
return colorX == null ? colorY = null : colorX.ToArgb() == colorY.ToArgb();
}
public virtual int GetHashCode(object x)
{
var colorX = (Color)x;
return (colorX != null) ? colorX.ToArgb() : 0;
}
public bool IsMutable { get { return false; } }
public object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return Color.FromArgb((int)NHibernateUtil.Int32.Get(rs, names[0]));
}
public void NullSafeSet(IDbCommand cmd, object value, int index)
{
NHibernateUtil.Int32.Set(cmd, ((Color)value).ToArgb(), index);
}
public object Replace(object original, object target, object owner)
{
return original;
}
public Type ReturnedType { get { return typeof(Color); } }
public SqlType[] SqlTypes { get { return new []{ SqlTypeFactory.Int32 }; } }
}
I went with an ICompositeUserType in the end.
Code as follows,
public class ColorType : ICompositeUserType
{
/// <summary>
/// Get the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <returns>
/// the property value
/// </returns>
public object GetPropertyValue(object component, int property)
{
var color = (Color) component;
if (property == 0)
{
return color.ToArgb();
}
return (int) color.ToKnownColor();
}
/// <summary>
/// Set the value of a property
/// </summary>
/// <param name="component">an instance of class mapped by this "type"</param>
/// <param name="property"/>
/// <param name="value">the value to set</param>
public void SetPropertyValue(object component, int property, object value)
{
throw new InvalidOperationException("Color is immutable");
}
/// <summary>
/// Compare two instances of the class mapped by this type for persistence
/// "equality", ie. equality of persistent state.
/// </summary>
/// <param name="x"/><param name="y"/>
/// <returns/>
public new bool Equals(object x, object y)
{
return ReferenceEquals(x, y) ||
x != null && y != null &&
object.Equals(x, y);
}
/// <summary>
/// Get a hashcode for the instance, consistent with persistence "equality"
/// </summary>
public int GetHashCode(object x)
{
return x == null
? 0
: x.GetHashCode();
}
/// <summary>
/// Retrieve an instance of the mapped class from a IDataReader. Implementors
/// should handle possibility of null values.
/// </summary>
/// <param name="dr">IDataReader</param>
/// <param name="names">the column names</param>
/// <param name="session"/>
/// <param name="owner">the containing entity</param>
/// <returns/>
public object NullSafeGet(IDataReader dr, string[] names,
ISessionImplementor session, object owner)
{
var argb = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[0]);
var knownColor = (int?) NHibernateUtil.Int32.NullSafeGet(dr, names[1]);
return knownColor != null
? Color.FromKnownColor((KnownColor) knownColor.Value)
: Color.FromArgb(argb.Value);
}
/// <summary>
/// Write an instance of the mapped class to a prepared statement.
/// Implementors should handle possibility of null values.
/// A multi-column type should be written to parameters starting from index.
/// If a property is not settable, skip it and don't increment the index.
/// </summary>
/// <param name="cmd"/>
/// <param name="value"/>
/// <param name="index"/>
/// <param name="settable"/>
/// <param name="session"/>
public void NullSafeSet(IDbCommand cmd, object value, int index,
bool[] settable, ISessionImplementor session)
{
var color = (Color) value;
if (color.IsKnownColor)
{
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
((IDataParameter) cmd.Parameters[index + 1]).Value = (int) color.ToKnownColor();
}
else
{
((IDataParameter) cmd.Parameters[index]).Value = color.ToArgb();
((IDataParameter) cmd.Parameters[index + 1]).Value = DBNull.Value;
}
}
/// <summary>
/// Return a deep copy of the persistent state, stopping at entities and at collections.
/// </summary>
/// <param name="value">generally a collection element or entity field</param>
/// <returns/>
public object DeepCopy(object value)
{
return value;
}
/// <summary>
/// Transform the object into its cacheable representation.
/// At the very least this method should perform a deep copy.
/// That may not be enough for some implementations,
/// method should perform a deep copy. That may not be enough for
/// some implementations, however; for example, associations must
/// be cached as identifier values. (optional operation)
/// </summary>
/// <param name="value">the object to be cached</param>
/// <param name="session"/>
/// <returns/>
public object Disassemble(object value, ISessionImplementor session)
{
return value;
}
/// <summary>
/// Reconstruct an object from the cacheable representation.
/// At the very least this method should perform a deep copy. (optional operation)
/// </summary>
/// <param name="cached">the object to be cached</param>
/// <param name="session"/>
/// <param name="owner"/>
/// <returns/>
public object Assemble(object cached, ISessionImplementor session, object owner)
{
return cached;
}
/// <summary>
/// During merge, replace the existing (target) value in the entity we are merging to
/// with a new (original) value from the detached entity we are merging. For immutable
/// objects, or null values, it is safe to simply return the first parameter. For
/// mutable objects, it is safe to return a copy of the first parameter. However, since
/// composite user types often define component values, it might make sense to recursively
/// replace component values in the target object.
/// </summary>
public object Replace(object original, object target, ISessionImplementor session, object owner)
{
return original;
}
/// <summary>
/// Get the "property names" that may be used in a query.
/// </summary>
public string[] PropertyNames { get { return new[] {"Argb", "KnownColor"}; } }
/// <summary>
/// Get the corresponding "property types"
/// </summary>
public IType[] PropertyTypes
{
get
{
return new IType[]
{
NHibernateUtil.Int32, NHibernateUtil.Int32
};
}
}
/// <summary>
/// The class returned by NullSafeGet().
/// </summary>
public Type ReturnedClass { get { return typeof (Color); } }
/// <summary>
/// Are objects of this type mutable?
/// </summary>
public bool IsMutable { get { return false; } }
}

Refresh HttpRuntime.Cache on Azure

Is there a way that i can refresh the HttpRuntime.Cache on Azure without having to Reboot the instance?
Thanks
When you're adding items to your cache, you should add them with a CacheDependency with a global key. This allows you to have your items expire after a given time but also to clear all items linked to this key.
Here is an example implementation of a method to clear all items (CacheProvider.cs):
/// <summary>
/// Caching provider
/// </summary>
public static class CacheProvider
{
const string CacheDependencyKey = "1FADE275-2C84-4a9b-B3E1-68ABB15E53C8";
static readonly object SyncRoot = new object();
/// <summary>
/// Gets an item from cache. If the item does not exist, one will be
/// created and added to the cache.
/// </summary>
/// <param name="key">Caching key</param>
/// <param name="valueFactory">Function to create the item of it does not exist in the cache.</param>
/// <param name="expiresAfter">Time after the item wille be removed from cache.</param>
public static TValue GetOrAdd<TValue>(string key, Func<TValue> valueFactory, TimeSpan expiresAfter)
{
object itemFromCache = HttpRuntime.Cache.Get(key);
if (itemFromCache == null)
{
lock (SyncRoot)
{
itemFromCache = HttpRuntime.Cache.Get(key);
if (itemFromCache == null)
{
TValue value = valueFactory();
if (value != null)
{
if (HttpRuntime.Cache[CacheDependencyKey] == null)
HttpRuntime.Cache[CacheDependencyKey] = string.Empty;
HttpRuntime.Cache.Add(key, value, new CacheDependency(null, new string[] { CacheDependencyKey }), DateTime.Now.Add(expiresAfter), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
return value;
}
}
}
return (TValue)itemFromCache;
}
/// <summary>
/// Invalidate all the items from the cache.
/// </summary>
public static void InvalidateCache()
{
HttpRuntime.Cache.Remove(CacheDependencyKey);
}
}
public void Clear()
{
var itemsToRemove = new List<string>();
var enumerator = HttpContext.Current.Cache.GetEnumerator();
while (enumerator.MoveNext())
{
itemsToRemove.Add(enumerator.Key.ToString());
}
foreach (string itemToRemove in itemsToRemove)
{
HttpContext.Current.Cache.Remove(itemToRemove);
}
}

How can I use a dynamic settings.Blah instead of AppSettings["blah"]?

I get how to use dynamic in C# 4.0, however, I'm not sure how to take something and make it dynamic-able (my technical term).
For example, instead of ConfigurationManager.AppSettings["blah"], how can I make a wrapper of sorts that will let me just use it like a dynamic: settings.Blah ?
You still need an entry point. However, from there the possibilities are quite flexible. This is an example idea to demonstrate how powerful dynamic dispatch can be:
public abstract class MyBaseClass
{
public dynamic Settings
{
get { return _settings; }
}
private SettingsProxy _settings = new SettingsProxy();
private class SettingsProxy : DynamicObject
{
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings[binder.Name];
if(setting != null)
{
result = setting.ToString();
return true;
}
result = null;
return false;
}
}
}
New version with generics !!
public static class WebConfiguration<T>
{
public static dynamic Get {
get { return _settings; }
}
static readonly SettingsProxy _settings = new SettingsProxy ();
class SettingsProxy : DynamicObject
{
public override bool TryGetMember (GetMemberBinder binder, out object result)
{
var setting = ConfigurationManager.AppSettings [binder.Name];
if (setting != null) {
result = Convert.ChangeType (setting, typeof(T));
return true;
}
result = null;
return false;
}
}
}
Usage
WebConfiguration<int>.Get.UserNameLength
Here, let me show you more powers of dynamic dispatch:
/// <summary>
/// Dynamic wrapper for <see cref="AppSettingsSection"/>.
/// </summary>
public class DynamicAppSettings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with configuration file
/// of the current application domain.
/// </summary>
public DynamicAppSettings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicAppSettings"/> class with the specified configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to configuration file.</param>
public DynamicAppSettings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the application settings entry from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.AppSettings.Settings[binder.Name];
return true;
}
/// <summary>
/// Adds or updates application settings entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.AppSettings.Settings[binder.Name] != null)
{
_config.AppSettings.Settings[binder.Name].Value = value.ToString();
}
else
{
_config.AppSettings.Settings.Add(binder.Name, value.ToString());
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> method to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.AppSettings.Settings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
/// <summary>
/// Dynamic wrapper for <see cref="ConnectionStringsSection"/>.
/// </summary>
public class DynamicConnectionStrings : DynamicObject
{
private string _exeConfigPath;
private System.Configuration.Configuration _config;
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the configuration file
/// of the current application domain.
/// </summary>
public DynamicConnectionStrings()
: this(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DynamicConnectionStrings"/> class with the specified
/// configuration file.
/// </summary>
/// <param name="exeConfigPath">Path to the configuration file.</param>
public DynamicConnectionStrings(string exeConfigPath)
{
if (string.IsNullOrEmpty(exeConfigPath))
throw new ArgumentNullException("exeConfigPath");
_exeConfigPath = exeConfigPath;
_config = ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = _exeConfigPath },
ConfigurationUserLevel.None);
}
/// <summary>
/// Gets the connection string value from the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString;
return true;
}
/// <summary>
/// Adds or updates a connection string entry in the configuration file.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (value != null && !(value is string))
{
return base.TrySetMember(binder, value);
}
if (_config.ConnectionStrings.ConnectionStrings[binder.Name] != null)
{
_config.ConnectionStrings.ConnectionStrings[binder.Name].ConnectionString = value.ToString();
}
else
{
_config.ConnectionStrings.ConnectionStrings.Add(
new ConnectionStringSettings(binder.Name, value.ToString()));
}
return true;
}
/// <summary>
/// Invokes <see cref="Configuration.Save"/> to save changes or removes an entry.
/// </summary>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name.Equals("Save", StringComparison.OrdinalIgnoreCase))
{
_config.Save();
result = null;
return true;
}
if (binder.Name.Equals("Remove", StringComparison.OrdinalIgnoreCase) &&
args != null && args.Length == 1 && (args[0] is string))
{
_config.ConnectionStrings.ConnectionStrings.Remove(args[0].ToString());
result = null;
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
}
For example:
dynamic apps = new DynamicAppSettings();
apps.UploadLocation = "~/Uploads/";
apps.WebServiceUrl = "http://www.site.com/service.svc";
apps.Remove("TempFolderPath");
apps.Save();
dynamic conns = new DynamicConnectionStrings();
conns.MasterDatabase = "Data Source=lpc:(local);Integrated Security=True;Initial Catalog=master";
conns.Save();

Categories

Resources