Dealing With JSON Object Of Dynamic Type

Hi Guys,

Been going nuts all morning. Simple simple simple problem. I have a json string that I want to deserialize into a class. The problem is it won’t deserialize, as one field is of type ‘object’. Json is being retreived by an API outside of my control, so I can’t change how it’s posted to me.

This is included in the total thing:

"preferences":[{"name":"auth_manual_passwordupdatetime","value":"1511800938"},{"name":"block_myoverview_last_tab","value":"courses"},{"name":"email_bounce_count","value":"1"},{"name":"email_send_count","value":"5"},{"name":"login_failed_count_since_success","value":"4"},{"name":"theme_adaptable_full","value":"fullin"},{"name":"theme_adaptable_zoom","value":"nozoom"},{"name":"_lastloaded","value":1518699801}]}]

This last bit

is what breaks everything, including JSONUtility, JSONObject, ect.

What’s the best way to auto format this? I was thinking of regular expressions, but it just feels to dirty. Any thoughts guys?

Thanks!

Is the json string exactly what you get back. Copied exactly and you didn’t leave anything out?

The values aren’t the issue if that is the case, however, you’ve got some odd brackets and appear to be missing some. For example.

{"preferences":[{"name":"auth_manual_passwordupdatetime","value":"1511800938"},{"name":"block_myoverview_last_tab","value":"courses"},{"name":"email_bounce_count","value":"1"},{"name":"email_send_count","value":"5"},{"name":"login_failed_count_since_success","value":"4"},{"name":"theme_adaptable_full","value":"fullin"},{"name":"theme_adaptable_zoom","value":"nozoom"},{"name":"_lastloaded","value":1518699801}]}

This is valid. Now, json.net on the asset store will handle this just fine, you could set “value” up as an object type. However, what I would do is just set it as a string type. It should convert the int into a string. Then if you need it as an int, you could use int.tryParse to see if it can be converted to an int and handle it as you need to.

Added tip: You can paste any json string into http://json2csharp.com/ and it will give you what values to setup and tell you if it’s a valid json. This works well with json.net.

2 Likes

Yeah that’s exactly what I did! and it seemed to like my output. It came out with this:

public class Customfield
{
    public string type { get; set; }
    public string value { get; set; }
    public string name { get; set; }
    public string shortname { get; set; }
}

public class Preference
{
    public string name { get; set; }
    public object value { get; set; }
}

public class RootObject
{
    public int id { get; set; }
    public string username { get; set; }
    public string firstname { get; set; }
    public string lastname { get; set; }
    public string fullname { get; set; }
    public string email { get; set; }
    public string department { get; set; }
    public int firstaccess { get; set; }
    public int lastaccess { get; set; }
    public string auth { get; set; }
    public bool suspended { get; set; }
    public bool confirmed { get; set; }
    public string lang { get; set; }
    public string theme { get; set; }
    public string timezone { get; set; }
    public int mailformat { get; set; }
    public string description { get; set; }
    public int descriptionformat { get; set; }
    public string country { get; set; }
    public string profileimageurlsmall { get; set; }
    public string profileimageurl { get; set; }
    public List<Customfield> customfields { get; set; }
    public List<Preference> preferences { get; set; }
}

which I converted to this:

namespace ELearning
{
    using System;
    using System.Collections.Generic;
    using UnityEngine;

    [Serializable]
    public class Customfield
    {
        [SerializeField] private string type;
        [SerializeField] private string value;
        [SerializeField] private string name;
        [SerializeField] private string shortname;
    }

    [Serializable]
    public class Preference
    {
        [SerializeField] private string name;
        [SerializeField] private object value;
    }
 
    [Serializable]
    public class User
    {
        [SerializeField] private int id;
        [SerializeField] private string username;
        [SerializeField] private string firstname;
        [SerializeField] private string lastname;
        [SerializeField] private string fullname;
        [SerializeField] private string email;
        [SerializeField] private string department;
        [SerializeField] private int firstaccess;
        [SerializeField] private int lastaccess;
        [SerializeField] private string auth;
        [SerializeField] private bool suspended;
        [SerializeField] private bool confirmed;
        [SerializeField] private string lang;
        [SerializeField] private string theme;
        [SerializeField] private string timezone;
        [SerializeField] private int mailformat;
        [SerializeField] private string description;
        [SerializeField] private int descriptionformat;
        [SerializeField] private string country;
        [SerializeField] private string profileimageurlsmall;
        [SerializeField] private string profileimageurl;
        [SerializeField] private List<Customfield> customfields;
        [SerializeField] private List<Preference> preferences;

But it would throw out the: “JSON must represent an object type” all the time. So it accepts ‘object’ as a type and will automatically work with that, or cast to string?

This is the exact output of the json (though I’ve removed my login details for privacy:

[
  {
    "id": 6,
    "username": "nigel",
    "firstname": "Nigel",
    "lastname": "",
    "fullname": "Nigel",
    "email": "nigel_.com",
    "department": "",
    "firstaccess": 1,
    "lastaccess": 1,
    "auth": "manual",
    "suspended": false,
    "confirmed": true,
    "lang": "en",
    "theme": "",
    "timezone": "99",
    "mailformat": 1,
    "description": "",
    "descriptionformat": 1,
    "country": "GB",
    "profileimageurlsmall": "http://www..co.uk/ecademy//image.php//core//u/f2",
    "profileimageurl": "http://www..co.uk//theme/.php//core/1516033544/u/f1",
    "customfields": [
      {
        "type": "checkbox",
        "value": "0",
        "name": "Food Allergies",
        "shortname": "FoodAllergies"
      }
    ],
    "preferences": [
      {
        "name": "auth_manual_passwordupdatetime",
        "value": "1511800938"
      },
      {
        "name": "block_myoverview_last_tab",
        "value": "courses"
      },
      {
        "name": "email_bounce_count",
        "value": "1"
      },
      {
        "name": "email_send_count",
        "value": "5"
      },
      {
        "name": "login_failed_count_since_success",
        "value": "4"
      },
      {
        "name": "theme_adaptable_full",
        "value": "fullin"
      },
      {
        "name": "theme_adaptable_zoom",
        "value": "nozoom"
      },
      {
        "name": "_lastloaded",
        "value": 1518708035
      }
    ]
  }
]

Lastly you say json.net will get that working. Do you know whether the built in Unity JsonUtility covers it?

Right, I suggest you switch to json.net, which uses properties instead of fields, but tends to be better when you have situations like this and can’t control the json string you get. It’s free on the asset store.

But, like I mentioned, if you are getting ints and strings for “value”, you should be able to set that as a string type in the class and any ints should be converted to strings (at least they do for me using json.net, jsonutility and others may not handle it the same)

1 Like

Right, limited JSONUtility it is. I can confirm it’s ‘object’ field which is the issue by the way. This hack fixes it:

protected string JsonRemoveJSObjects(string input)
        {
            // Most hacky hack there was ever to hack. Never show this to anyone.... lol
            int index = input.IndexOf("_lastloaded");

            if(index != -1)
            {
                string output = input.Substring(1, index - 1);
                output += "_lastloaded\",\"value\":\"IGNORE_THIS\"}]}";
                return input;
            }
            return input;
        }

I’ll check out json.net. Thanks!

Okay, for anyone else checking this out. Do one of the following:

  1. Use json.net and copy from the website.

  2. Create an ISerializationCallbackReceiver for System.object. The beenefit being you don’t need an extra third party dependency and don’t need to break SOLID principles of encapsulation to satisfy the utility.