2

How do I convert Json.NET objects to conventional .NET types (JArray of string to List<string>, JTokenType=Integer to int, etc.)? I have found little besides suggestions to use AutoMapper or JToken.ToObject<T>. This is good advice when the JSON structure is known at compile time, but I can't create a class to represent unknown data, or specify a conversion when I don't know the underlying type. Json.NET doesn't have a "ConvertToWhateverIsProbablyMostAppropriate()" member. :)

So why not just enumerate through JWhatever objects, leaving them as-is? Because I can't pass those types as parameters to (say) Dapper, which doesn't know JToken from spoo.

mason
  • 31,774
  • 10
  • 77
  • 121
Charles Burns
  • 10,310
  • 7
  • 64
  • 81

2 Answers2

4

There's no need for the long function in Charles'/your answer. Just use the generic ToObject function and use the object type parameter.

Complete Example:

using System;
using Newtonsoft.Json.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            string input = "Hello, world!";
            JToken token = JToken.FromObject(input);
            object output = token.ToObject<object>();
            Console.WriteLine(output);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

That said, you should know what type it is, and I can't really think of a situation where you wouldn't know what type it is and can't convert it to something more appropriate with a more specific type parameter in the call to ToObject.

mason
  • 31,774
  • 10
  • 77
  • 121
  • 1
    "`I can't really think of a situation where you wouldn't know what type`" I get that a lot. Library authors in particular seem to have the same conclusion. – Charles Burns Apr 16 '16 at 05:34
  • 1
    ToObject does indeed simplify the function, though only for some data types. I originally wrote code to convert JArrays, for which ToObject is not very helpful, and assumed that the same applied for simple data types.Cue lesson about assuming things. :) – Charles Burns Apr 16 '16 at 05:43
  • @Charles I'm not a library author, I'm a full stack web developer. What situation are you dealing with where you don't know what the type is? – mason Apr 16 '16 at 13:08
  • The specific code I was working on receives JSON containing updated data for a DB table it knows nothing about. Each item in this data may be for a concrete column in the table or perhaps for a virtual column created through a join which might be an array of any type. While I can detect the DBMS column type, each DBMS has different types and rules and many support custom types. I want to handle this generically. Most libraries assume I know all about the data involved, hence the note about library authors, not you specifically. Not that I wouldn't do the same -- this is hardly a typical case. – Charles Burns Apr 16 '16 at 16:19
  • If you edit your answer to address objects and arrays, I'll mark it as the answer. – Charles Burns Apr 18 '16 at 16:35
3

I wrote the following method to convert JToken to good old conventional .NET types. This is more thorough than I need (to handle only a few JTokenTypes), but I extended it for this answer.

Caveat discipulus: This code is untested and may be a poor implementation of the worst possible approach to a problem that doesn't exist.

    /// <summary>Converts a Json.Net JToken to a boxed conventional .NET type (int, List, etc.)</summary>
    /// <param name="token">The JToken to evaluate</param>
    public object JTokenToConventionalDotNetObject(JToken token)
    {
        switch(token.Type) {
            case JTokenType.Object:
            return ((JObject)token).Properties()
                .ToDictionary(prop => prop.Name, prop => JTokenToConventionalDotNetObject(prop.Value));
            case JTokenType.Array:
            return token.Values().Select(JTokenToConventionalDotNetObject).ToList();
            default:
            return token.ToObject<object>();
        }
    }

To handle JArrays, my original problem, Json.NET again makes the task simple:

    /// <summary>Converts a Json.NET JArray into a List of T where T is a conventional .NET type (int, string, etc.)</summary>
    /// <param name="jArray">Json.NET JArray to convert</param>
    public IList<object> JArrayToList(JArray jArray) {
        return (List<object>)jArray.ToObject(typeof(IList));
    }

Input type: JArray of Newtonsoft.Json.Linq.JValue with JTokenType of Integer

Output: List<object> where each object is of type System.Int64

I believe that Json.NET's ToObject behavior is not always obvious. Given conversion type <Object>, it returns either a conventional .NET type (long, string) or does nothing at all, e.g. gets and returns Newtonsoft.Json.Linq.JArray, depending on the JTokenType.


Edit: Simplified code with @mason's help and with code from the SO question for which mine is marked duplicate. Now that I better understand Json.NET's types work, I see that answer was sufficient.

The salient difference between the answers is only that that this code handles nested arrays/objects.

Charles Burns
  • 10,310
  • 7
  • 64
  • 81