0

I'm using CodeFluent JsonUtilities to convert an object to JSON. Using anything else seems to have various other issues (e.g. Circular Referencing).

Here's some functions I use to convert to JSON for ASP.NET MVC, using CodeFluent.Runtime.Utilities namespace (for the JsonUtilities).

    public static ContentResult ConvertToJsonResponse(object obj)
    {
        string json = JsonUtilities.Serialize(obj);
        return PrepareJson(json);
    }

    /// <summary>
    /// Converts JSON string to a ContentResult object suitable as a response back to the client
    /// </summary>
    /// <param name="json"></param>
    /// <returns></returns>
    public static ContentResult PrepareJson(string json)
    {
        ContentResult content = new ContentResult();
        content.Content = json;
        content.ContentType = "application/json";

        return content;
    }

The problem is when I use JsonUtilities to convert an object it seems to have skipped some nested objects.

For example, I tried to convert DataSourceResult object (from Telerik) to JSON using CodeFluent.

public ActionResult UpdateTeam([DataSourceRequest]DataSourceRequest request, TeamViewModel teamViewModel)
{
    ModelState.AddModelError("", "An Error!");
    DataSourceResult dataSourceResult = new[] { teamViewModel }.ToDataSourceResult(request, ModelState);
    ContentResult ret = CodeFluentJson.ConvertToJsonResponse(dataSourceResult);
    return ret;
}

The dataSourceResult holds three main properties:

  1. Data - which hold my Model that contains my data.
  2. Total - which holds the amount of data objects there are.
  3. Errors - which holds all the errors of my MVC model. It is very nested, with a lot of properties under it.

When I try to use CodeFluent utilities to convert the DataSourceResult object it works to convert "Data", and "Total" fields, but with Errors, it skips it entirely, resulting in the below JSON string:

{  
   "Data":[  
      {  
         "ExampleProperty":"ExampleValue"
      }
   ],
   "Total":1,
   "AggregateResults":null,
   "Errors":{  }
}

I'm guessing the issue is with the "Errors" object being too nested for the CodeFluent converter. So my question is are there any CodeFluent serialization options/code I'm missing to work with heavily nested objects for JSON conversion?

MTran
  • 1,799
  • 2
  • 17
  • 21
  • Can you detail what class is DataSourceResult ? From what telerik product? – Simon Mourier Nov 29 '15 at 08:54
  • See this Link: https://doylestowncoder.wordpress.com/2014/04/14/kendoui-understanding-todatasourceresult/ Telerik DataSourceResult object is from Telerik ASP.NET MVC. It a default class to format my response to send to Telerk ASP.NET MVC Grid (and possibly other controls that uses the DataSource object). For read operations on my MVC Grid, it does all my paging, sorting, filtering and formats my model objects and model state errors to be sent back to the MVC Grid for it to handle CRUD operations. For create/update/delete, it's mostly just for error handling purposes as far as I know. – MTran Nov 29 '15 at 09:41

1 Answers1

1

The problem comes from the fact you're creating a model error with an empty key. It's not forbidden but JsonUtilities just skip dictionary values with an empty key, by design.

Just use a real key, like this:

ModelState.AddModelError("my first error", "An Error!");
ModelState.AddModelError("my second error", "An Error!");

And you'll see the errors collection serialized, like this:

{
 "Data":[  
  {  
     "ExampleProperty":"ExampleValue"
  }],
 "Total": 1,
 "AggregateResults": null,
 "Errors": {
   "my first error": {
     "errors": [
       "An Error!"
      ]
    },
   "my second error": {
     "errors": [
       "An Error!"
      ]
    }
  }
}

Otherwise, if you really want to keep empty keys, then you can leverage the JsonUtilitiesOptions class that has callbacks for tweaking the serialization (and deserialization) process. Careful with this as you can easily break JSON syntax. This is how you could do it, in a way that should handle all cases:

JsonUtilitiesOptions options = new JsonUtilitiesOptions();
options.WriteValueCallback += (e) =>
{
    IDictionary valueDic = e.Value as IDictionary;
    if (valueDic != null)
    {
        e.Writer.Write('{');
        bool first = true;
        foreach (DictionaryEntry entry in valueDic)
        {
            if (!first)
            {
                e.Writer.Write(',');
            }
            else
            {
                first = false;
            }

            // reuse JsonUtilities already written functions
            JsonUtilities.WriteString(e.Writer, entry.Key.ToString(), e.Options);
            e.Writer.Write(':');
            // object graph is for cyclic/infinite serialization checks
            JsonUtilities.WriteValue(e.Writer, entry.Value, e.ObjectGraph, e.Options);
        }
        e.Writer.Write('}');
        e.Handled = true; // ok, we did it
    }
};
string json = JsonUtilities.Serialize(obj, options);

Now, you'll get this result:

{
 "Data":[  
  {  
     "ExampleProperty":"ExampleValue"
  }],
 "Total": 1,
 "AggregateResults": null,
 "Errors": {
   "": {
     "errors": [
       "An Error!"
      ]
    }
  }
}
Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • This works, but are there any serializer options which I can set it to not skip dictionary values with empty keys? Or is this option not provided? It would be useful to not put stub keys since my JavaScript that takes this JSON object uses those keys in "Errors" to indicate which fields has errors. In my case, it's a custom error not specific to a field. – MTran Nov 29 '15 at 23:19
  • You could do it with options (define a JsonUtilitiesOptions and define callbacks on it), but it would also require some extra code. But you're right the empty string seems legit in json as a dictionary key (although a bit strange). We'll change the serializer so it doesn't skip empty (not null) dictionary keys. Note that like other keys, you can have only one empty key in a given dictionary. – Simon Mourier Nov 30 '15 at 06:23
  • "You could do it with options (define a JsonUtilitiesOptions and define callbacks on it)". I'm familiar with setting serialization options using JsonUtilitiesOptions, how how do I define callbacks on it to get the effect I'm looking for? – MTran Dec 01 '15 at 21:28
  • 1
    @Oniisaki - the latest (yesterday) release includes handling of empty & non-null keys. – Simon Mourier Dec 04 '15 at 10:39
  • Thanks a lot. I tried the updated code and it now works perfectly with empty keys. – MTran Dec 13 '15 at 04:56