Thoughts on Dyanmic

Sep 13, 2010 at 9:58 PM

I had a few thoughts on the use of dynamic. I am currently developing a project called the Facebook C# SDK that uses dyanmic to access the Facebook APIs. There are a couple of things I would like to see in Json.Net.

First, direct value access using dymamic.

 

dynamic result = JsonConvert.DeserializeObject("json-string-value");
int id = result.id;
bool tf = result.something;
long myNumber = result.my_number;

Second, I would like to see an option that would allow accessing invalid properties on a dynamic object to not throw. Here is what I mean.

dynamic result = JsonConvert.DeserializeObject("json-string-value");
// Normal behavior
var validProperty = result.valid_property;
// Access first layer invalid property
var invalidProp = result.invalid;
// normally this would throw a RuntimeBinderException. In my implementation it actually returns another dynamic type called InvalidProperty and does not throw.

// You can use invalid property like this:

if (result.invalid != InvalidProperty.Insance) {
   // The property is valid
} else {
  // The property is invalid, but does not throw
}

// Similarly, on a nested invalid property.

if (result.invalid.invalid2 == InvalidProperty.Instance) {
   // Handle the invalid property.
}

// Or for web services like facebook that don't return properties that have no value.

string first_name;
if (result.first_name != InvalidProperty.Instance) {
   first_name = result.first_name;
}

//I also have my invalid property cast to default values
bool b = (bool)result.InvalidProperty // false
int i = (int)result.InvalidProperty // 0
string s = (string)result.InvalidProperty // string.Empty


Just some thoughts. Keep up the good work, this is a great library!

Coordinator
Sep 13, 2010 at 11:37 PM

Check out the latest source code - I added support during the weekend.

http://json.codeplex.com/SourceControl/list/changesets

Sep 14, 2010 at 1:55 PM
Edited Sep 14, 2010 at 2:17 PM

I did look at the new source code, and like it so far, but I was wondering about a few points specifically. First, regarding the invalid properties. What about rather than returning null when an invalid property is accessed returning some sort of invalid property class. The reason I like this approach is to facilitate the following:

            dynamic result = app.Api("/331218348435");
            Assert.AreEqual(null, result.venue.badname); // this passess
            Assert.AreEqual(null, result.venue.badname.badname); // this throws a RuntimeBinderException (Cannot perform runtime binding on a null reference)
Personally, I dont like the inconsistency of this. A first level invalid property just returns null, but a second level throws. If the first level invalid property returned a dynamic type, it would be possible to infinitely nest the properties. Such as:

dynamic something = result.invalid.invalid.bad.bad.bad; 

Additionally, it would allow you to check if a property is invalid rather than null. Sometimes a valid property could be null. For example:
dynamic optionalProperty = result.optional;
if (optionalProperty != null) {
   Console.Write("The property has a value of" + optionalProperty);
}

// invalid property
dynamic invalidProperty = result.invalid;
if (invalidProperty == InvalidProperty.Instance) {
   Console.Write("The property is not valid.");
}

The second question is about casting. Is it possible to access a value directly or do all properties have to be cast? In my opinion the desired behavior would be:
long l = result.id; // desired
long l = (long)result.id // less desirable

Anyway, thanks! I look forward to the .net 4.0 release.

 

Coordinator
Sep 14, 2010 at 10:45 PM
Edited Sep 14, 2010 at 11:12 PM

If a property isn't set then I want it to return null. This is consistent with how a regular JObject works.

Along those same lines I don't see anything wrong with accessing a member on a null reference throwing an exception - again consistent with normal .NET code. Note that Json.NET doesn't return null for a property with a value of null, it returns a JValue with a type of null for when you want to differentiate between the two.

Casting happens automatically, following your desirable example.

To summarize, my goal with dynamic support is to add properties on a JObject and to have casting of values happen automatically in both directions. Other than those two features I want it to be as similar to normal .NET code as possible.

Sep 14, 2010 at 11:41 PM

I appreciate the response. I think that the behavior with the null values and properties is close enough for what I am aiming for in the Facebook Sdk. I think I am going to rely on your library's return types now that you support dynamic. I don't see any sense in duplicating functionality when your library works so well in regards to handling json objects. 

Best,

Nathan Totten

Sep 16, 2010 at 3:20 PM

James, one last question. Thanks for putting up with me!

So I made a branch of my Facebook C# SDK project that uses your newest code for serialization and I have one issue I cant seem to figure out regarding the casting. Basically, from what I have noticed from the code, implicit casting is actually not supported. So when I try to do something like:

            dynamic pageResult = app.Api("http://www.example.com");
            int i1 = (int)pageResult1.shares; // works
            int i2 = pageResult1.shares; // invalid cast exception. shares is a JProperty
 I would rather have shares just return an int rather than a JProperty with an int value or a type that implicitly casts to its correct type. I know it would be a lot of work, but would it maybe make sense to have classes like JIntegerProperty, JStringProperty, etc. which have implicit casts?

Sep 16, 2010 at 3:35 PM

Would it make sense to return a generic JProperty<T> where T is the type of the value. Here would be a possible implementation of that:

namespace Newtonsoft.Json.Linq
{
    /// <summary>
    /// Represents a JSON property.
    /// </summary>
    public class JProperty<T> : JProperty
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="JProperty"/> class from another <see cref="JProperty"/> object.
        /// </summary>
        /// <param name="other">A <see cref="JProperty"/> object to copy from.</param>
        public JProperty(JProperty other)
            : base(other) { }

        internal JProperty(string name) : base(name) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="JProperty"/> class.
        /// </summary>
        /// <param name="name">The property name.</param>
        /// <param name="content">The property content.</param>
        public JProperty(string name, params object[] content)
            : base(name, (object)content) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="JProperty"/> class.
        /// </summary>
        /// <param name="name">The property name.</param>
        /// <param name="content">The property content.</param>
        public JProperty(string name, object content) : base(name, content) { }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Newtonsoft.Json.Linq.JProperty<T>"/> to T.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator T(JProperty<T> property)
        {
            return (T)property;
        }
    }
}