JSON into Unity Class

Hello,

I am retrieving a JSON message dynamically from an external source. Its a message list;

{  
   "conversation":{  
      "-LD8kInJ39jHQfrbjz34":{  
         "message":"msg1",
         "read":false,
         "sender":"X8oNADtJmOVSouYNU450d",
         "timestamp":"05/22/2018 22:28:52"
      },
      "-LDBzfk8W-DnGn9dMptw":{  
         "message":"msg2",
         "read":false,
         "sender":"X8oNADtJmOVSouYNU450d",
         "timestamp":"05/23/2018 13:34:54"
      },
      "-LDBzgy9gfQTBk1hi0MT":{  
         "message":"msg3",
         "read":false,
         "sender":"X8oNADtJmOVSouYNU450d",
         "timestamp":"05/23/2018 13:34:59"
      }
   }
}

I need to then convert this into an Object-Class within Unity. Usually, I do this by using Unity’s built in JsonUtility.FromJson.

My Class is a List, within another Class that actually holds the structure.

[System.Serializable]
public class ConversationClass{

	public string message;
	public string sender;
	public string timestamp;
	public bool read;
}

The problem is the generated index keys; “-LD8kInJ39jHQfrbjz34” for example. As these break the List array as an index, and I get Unexpected node type.

Any suggestions?

Is there a quick, easy way in which I can reference the keys, and then replace with “0”, “1”, “2”, etc ?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

[Serializable]
public class Wrapper
{
    public List<ConversationClass> conversation = new List<ConversationClass> { new ConversationClass(),new ConversationClass(),new ConversationClass() };
}

[Serializable]
public class ConversationClass
{
    public string message;
    public string sender;
    public string timestamp;
    public bool read;
}

public class Deserialize : MonoBehaviour {

    
    public string path;

	// Use this for initialization
	void Start ()
    {
        var json = JsonUtility.ToJson(new Wrapper(),true);
        path = Application.dataPath + "/json.json";
        System.IO.File.WriteAllText(path,json);	
        
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

If you put your json in json viewer is read like a class, conversation is a class and not a list, so if you want to have a list you should have a wrapper class, and into this, a list of conversation class. Your key now are variable. you should insert the key into a conversation class, in this way you can retrieve your key without problem

I accomplished this using SimpleJSON in the end;
[http://wiki.unity3d.com/index.php/SimpleJSON][1]

I basically just serialised it myself;

var N = JSON.Parse(_json);

for(int i = 0; i < N["conversation"].Count; i++){

	MessageClass.Message.ConversationClass _conversation = new MessageClass.Message.ConversationClass();
	_conversation.message = N["conversation"]*["message"].Value;*

_conversation.sender = N[“conversation”][“sender”].Value;
_conversation.timestamp = N[“conversation”][“timestamp”].Value;
conversation.read = N[“conversation”][“read”].AsBool;
* Messages.Contents.conversation.Add( conversation );*
}

*[1]: http://wiki.unity3d.com/index.php/SimpleJSON*_

Why something so mature as serialization has to be such a pain here with Unity is very frustrating. I’d recommend you check out Json.Net - there are multiple free versions on the store.


You will be able to find lots of help for it on StackOverflow/Google. Unfortunately these unity versions are a little bit behind the offical versions and so are lacking a few features, but its still lightyears ahead of anything else, even Microsoft has adopted it as the standard for .Net Core.


Here’s my current code, for underscore formatting.

using Newtonsoft.Json;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Serialization;

namespace BackendSystem
{
    public interface ISerializationProvider
    {
        T Deserialize<T>(string text);
        string Serialize<T>(T obj);        
    }
        
    public class BackendSerializer : ISerializationProvider
    {
        public BackendSerializer()
        {
            JsonConvert.DefaultSettings = () => new JsonSerializerSettings
            {
                ContractResolver = new UnderscorePropertyNamesContractResolver(),
                NullValueHandling = NullValueHandling.Ignore
            };    
        }
        
        public T Deserialize<T>(string text)
        {
            return JsonConvert.DeserializeObject<T>(text);
        }

        public string Serialize<T>(T model)
        {
            return JsonConvert.SerializeObject(model);
        }
 
        private class UnderscorePropertyNamesContractResolver : DefaultContractResolver  
        {
            protected override string ResolvePropertyName(string propertyName)
             => Regex.Replace(propertyName, @"(\w)([A-Z])", "$1_$2").ToLower();
        }    
    }
}

namespace BackendSystem
{
    public static class Backend
    {
        public static ISerializationProvider Serializer { get; set; } = new BackendSerializer();   
    }           
}

And use it…

var errorResponse = Backend.Serializer.Deserialize<ErrorResponse>(www.downloadHandler.text);

another example.

result.ResponseSize = www.downloadedBytes;
result.Received = DateTime.Parse(www.GetResponseHeader("Date"));
result.StatusCode = (HttpStatusCode) www.responseCode;
result.Value = Backend.Serializer.Deserialize<T>(www.downloadHandler.text);

etc

var data = Backend.Serializer.Serialize(new RequestModel
{
     Provider = o.Provider,
     AccessToken  = o.Token,
     Resource  = o.Resource ?? Backend.Options.ResourceName,
});   

EDIT: Just realized that i didnt answer your question. You can do what you want with Json.Net like this.

[System.Serializable]
public class ConversationClass
{ 
    public string message;
    public string sender;
    public string timestamp;
    public bool read;
}

void Start()
{            
    var json = @"{  
        ""conversation"":{  
            ""-LD8kInJ39jHQfrbjz34"": {  
                ""message"":""msg1"",
                ""read"":false,
                ""sender"":""X8oNADtJmOVSouYNU450d"",
                ""timestamp"":""05/22/2018 22:28:52""
            },
            ""-LDBzfk8W-DnGn9dMptw"": {  
                ""message"":""msg2"",
                ""read"":false,
                ""sender"":""X8oNADtJmOVSouYNU450d"",
                ""timestamp"":""05/23/2018 13:34:54""
            },
            ""-LDBzgy9gfQTBk1hi0MT"": {  
                ""message"":""msg3"",
                ""read"":false,
                ""sender"":""X8oNADtJmOVSouYNU450d"",
                ""timestamp"":""05/23/2018 13:34:59""
            }
        }
    }";
    
    var jsonObject = JObject.Parse(json);
    var conversationsEntries = jsonObject["conversation"].Children();           
    var conversations = conversationsEntries.Select(t => t.FirstOrDefault()?.ToObject<ConversationClass>());
}

Instead of converting the conversations into a List(ConversationClass) you could try converting it into a Dictionary(string, ConversationClass) . If that works it seems the easiest/simplest solution and should only require a few lines of code to be changed.