Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

JSON .NET for Unity

Discussion in 'Assets and Asset Store' started by Dustin-Horne, Sep 13, 2013.

  1. BrUnO-XaVIeR

    BrUnO-XaVIeR

    Joined:
    Dec 6, 2010
    Posts:
    1,687
    Last edited: Feb 3, 2014
  2. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh I see what you're saying. The DataContractSerializer is something that's built in to .NET and really has nothing to do with JSON .NET. That class isn't available within Unity.

    Could you give me a link to the API(s) you're trying to use? I'll do a little research and see if it's possible to specify a different serializer to use. If so, then my asset will work for you, but if it's not possible to specify the serializer then it won't help you.
     
  3. BrUnO-XaVIeR

    BrUnO-XaVIeR

    Joined:
    Dec 6, 2010
    Posts:
    1,687
  4. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh I see, you do the deserialization yourself. In that case, yes my asset will work just fine. As an example... In the "Translate" method their example looks like this:

    Code (csharp):
    1.  
    2. using (Stream stream = response.GetResponseStream())
    3.                 {
    4.                     System.Runtime.Serialization.DataContractSerializer dcs = new System.Runtime.Serialization.DataContractSerializer(Type.GetType("System.String"));
    5.                     string translation = (string)dcs.ReadObject(stream);
    6.                     Console.WriteLine("Translation for source text '{0}' from {1} to {2} is", text, "en", "de");
    7.                     Console.WriteLine(translation);
    8.  
    9.                 }
    10.  
    To deserialize it using JSON .NET instead, you would do the following:

    Code (csharp):
    1.  
    2.  
    3. using (Stream stream = response.GetResponseStream())
    4. {
    5.         var reader = new StreamReader(stream);
    6.         var translation = JsonConvert.DeserializeObject<string>(reader.ReadToEnd());
    7.  
    8.         Console.WriteLine("Translation for source text '{0}' from {1} to {2} is", text, "en", "de");
    9.         Console.WriteLine(translation);
    10. }
    11.  
    12.  

    And for AdminAccessToken they have this example:
    Code (csharp):
    1.  
    2. using (WebResponse webResponse = webRequest.GetResponse())
    3.             {
    4.                 DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AdmAccessToken));
    5.                 //Get deserialized object from JSON stream
    6.                 AdmAccessToken token = (AdmAccessToken)serializer.ReadObject(webResponse.GetResponseStream());
    7.                 return token;
    8.             }
    9.  
    With JSON .NET you do the following:

    Code (csharp):
    1.  
    2. using (WebResponse webResponse = webRequest.GetResponse())
    3. {
    4.      
    5.       var reader = new StreamReader(webResponse.GetResponseStream());
    6.       var token = JsonConvert.DeserializeObject<AdmAccessToken>(reader.ReadToEnd());
    7.  
    8.        return token;
    9. }
    10.  
     
  5. BrUnO-XaVIeR

    BrUnO-XaVIeR

    Joined:
    Dec 6, 2010
    Posts:
    1,687
    Nice, I will try it. Thanks
     
  6. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Sorry for the dumb question!

    But there is some example of how I can create a method to save and load my scene objects using JSON .NET?
     
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    What exactly is it that you want to save and load? You won't be able to directly serialize a GameObject or MonoBehaviour but you can create proxies for saving the data.
     
  8. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    I'm trying to serialize an entire object, is that possible? It results in the below being thrown.

    Code (csharp):
    1.  
    2. UnassignedReferenceException: The variable customPreviewImage of 'OrientedBrush' has not been assigned.
    3. You probably need to assign the customPreviewImage variable of the OrientedBrush script in the inspector.
    4. (wrapper dynamic-method) UnityEngine.Texture2D.GetmipmapCount (object) <IL 0x00006, 0x00040>
    5. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (object) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:102)
    6. Rethrow as JsonSerializationException: Error getting value from 'mipmapCount' on 'UnityEngine.Texture2D'.
    7. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (System.Object target) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:106)
    8. Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContract collectionValueContract) (at Assets/JsonDotNet/Source/Serialization/JsonSerializerInternalWriter.cs:332)
    9. UnityEditor.DockArea:OnGUI()
    10.  
     
  9. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,459
    Would be Super Awesome if you could provide working examples of various saving/loading proxies so that we can better understand how to use it.

    Could I make a request for these examples to be included in a near future update? or a link to the example packages that we can download ?

    Cheers.
     
  10. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Yes... I have a scene with some prefabs which were instanciated in runtime... I need a way to save a list with these objects and recreate them with they actual state in a later time.

    Can it save all my public properties? How do I save a list to use as reference to recreate everything later?

    A simple example of how to do something like this would be Super Awesome! =)
     
  11. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    I'm trying to generate a Windows Phone 8 build by I got this error:

    Assets/JsonDotNet/Source/Serialization/JsonTypeReflector.cs(348,16): error CS0103: The name `LateBoundReflectionDelegateFactory' does not exist in the current context

    This error trying Windows Store App Build

    Assets\JsonDotNet\Source\WinRT\Serialization\RT_DefaultContractResolver.cs(113,13): error CS0104: 'BindingFlags' auma referencia ambigua entre 'System.Reflection.BindingFlags' e 'Newtonsoft.Json.Utilities.BindingFlags'
     
    Last edited: Feb 12, 2014
  12. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568

    What type of object are you trying to serialize? This is similar to what happens if you try to serialize a GameObject because it tries to access rigidBody. The Unity docs say it should return null but it actually doesn't so the serializer tries to serialize it, then Unity throws an exception because you're accessing properties and it hasn't been added / initialized.
     
  13. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568

    Sure, I'm actually working on some offline documentation to be included and I'll put some of this in there. There is an example a little while back that we did with Texture2D, but I can add some additional samples.
     
  14. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I'll try to put together a sample for you. Do you have an example?
     
  15. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    What version of Unity are you using? There are two things... there is a bug in LateBoundReflectionDelegateFactory. At the very top of that file add:

    || UNITY_WP8

    That will fix that problem... that will be included in the next update.

    As for the BindingFlags issue, you are likely going to run into the same problem with Windows Phone (and others). In 4.3.3, Unity added a bunch of missing framework items... one of those was BindingFlags so I have to find a workaround since that was accounted for in the JSON .NET library.

    Also, they screwed up Type so things like Type.GetTypeCode don't work. It is still broken in 4.3.4 and they have said they don't intend to fix it in the 4.3.x but it will be fixed in the next major version. There is a workaround for it for now and it's the only way to fix it:

    http://forum.unity3d.com/threads/223065-Unity-4-3-3-Type-GetMembers(BindingFlags)-crash?p=1509391#post1509391
     
  16. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,459
    @Dustin,

    It's been really nice to use Json.net.

    I'm trying to learn how create delegates and events dynamically on IOS and was googling to find out how to do reflection on IOS. While using Json.net to save some data, it dawned on me that Json.net is a great example how to master the AOT problem.

    If time permits and you feel like blogging, it would be so awesome if you could share some of your experiences for dealing with reflection code for AOT platform. Most appreciative.

    Cheers.
     
    Last edited: Feb 12, 2014
  17. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Sure! There is a lot actually... not sure where to even begin. There are issues that are specific to Unity implementation and issues that are specific to AOT and things it doesn't support from a reflection standpoint which have been very frustrating. One example is generic interfaces or classes. Let's say you have a class called: SomeClass<T> and you inherit from it, so you create DerivedClass : SomeClass<Vector3>. This will create havoc with AOT because it can't figure out what type to pass into SomeClass<T> even though you've explicitly defined it. Also, things like Dictionaries... if you haven't instantiated an instance of a class or dictionary yourself at any point before deserializing, the AOT compiler can't figure out how to execute the constructor via reflection.
     
  18. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    Hey there, Dustin!
    Your plugins looks great. I have one important question though - does it supports yielding of both serialization and deserialization? And how fast it performs at all, comparing to fastJSON?

    Asking because I tried to use JsonFX and it works relatively slow, causing huge frame drops on first serialize / deserialize (or coercing) on mobile.
    fastJSON works about a 2x faster, but it fails to deserialize some objects in my case and it still has not yieldable heavy functions which causes significant frame drops on mobile as well.
     
  19. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Good question... JSON .NET does use some type / assembly caching to speed up its execution. It performs faster than Microsoft's JavaScriptSerializer and the DataContractSerializer but I've not run any actual tests against anything. If you have some tests you setup for fastJSON that you can send over to me I can certainly add in JSON .NET and check for you. As for yielding, I'm not sure exactly what you mean, but if you mean performing callbacks when certain things are serialized and deserialized, take a look at the documentation here:

    http://james.newtonking.com/json/help/?topic=html/SerializationCallbacks.htm
     
  20. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    I did tested it in pretty big project in conjunction with server, on real data. Unfortunately I did not set up any tests for this yet (but looks like I will do this in future).
    By yielding I meant async execution.
    I used both JsonFX and fastJSON within coroutines of course already (and even yielded their execution from IEnumerator Start, but I described results in previous post - I still see frame drops, even if parsed data was really small, less then 1kb), as addition I can say all deserialization / serialization were running in single frame each, so these processes are not async. And it happens only on first run, any further actions with JSON works pretty smooth ([de]serialization still runs in single frame though, but not for so long as first time), so I guess it's something huge at the initialization phase (on first use) and could be great to have plugin which performs initialization and further actions completely asynchronously, making mobile usage smooth..
     
  21. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    you may see this same issue with any serializer you use as it might be caused by the initial assembly resolution but I can't be sure. One thing to keep in mind is that if you're using classes and not structs, you're going to be allocating as well when you deserialize so that may be as much a cause of your stutter as the serializers themselves.

    As for async, I need to check... I had to use an older version of JSON .NET to get it compatible with iOS in Unity. In version 4.5, Async serialization and deserialization was added to JSON .NET but I'm not sure if it exists in this version. I need to check on that for you this evening when I'm not at work.

    One thing to note, at some point in the future I plan to update the whole library to Version 5 (the current version) if I can figure out how to make it AOT compatible but it is a massive undertaking. However, currently HashSet isn't supported in iOS and Multidmensional arrays are not supported, but if I get it updated to V5, the multidimensional arrays will be supported and possibly HashSet<T> on iOS though I'm not positive on the second one.

    The reason it's such a challenge is because the entire architecture of JSON .NET changed between V4 and V5. From an enduser standpoint it's exactly the same, but under the hood it's completely different.
     
  22. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    I see, thanks for your answers, Dustin!
     
  23. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You're very welcome. :) I'll take a look this evening and see if the async functionality is there. I'm in Sprint / Iteration planning all day today so I don't have access to look at it.
     
  24. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    It's a Brush object from RotorzTileSystem. I tried setting the settings for the converted to ignore null, etc.. but that didn't seam to resolve it. From the error it looks like it maybe having issues with a Texture2D? I don't know for sure. The variable customPreviewImage it's erroring on is a Texture2D (or null).
     
  25. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Hmm... If you could put together a repro for me that would be great. My guess though is that the RotorzTileSystem has a property getter that automatically throws the exception if the property is not set. This wouldn't surprise me at all, so you might have to code around that. If it's a Texture2D you'll run into problems trying to deserialize also because Texture2D doesn't have a parameterless constructor that's public. If your read through this forum a couple pages back I showed an example of how to handle custom serialization and even used Texture2D as an example.
     
  26. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    Is it possible to just give it a list of properties to completely ignore? There's a few I don't really need or don't make sense to serialize so it'd be good to tell it which ones to ignore.
     
  27. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
  28. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    Oh, I see. Was hoping to just send it an array of properties to ignore, lol. I'm not sure if that's possible to add, but it'd be pretty neat as many of my assets are inside of DLLs, etc.. Basically to have that custom contract resolver built in (seams to just take a list of properties and ignore them if they exist). At any rate I'll give implementing it a try.
     
  29. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    Ok, I got the below working to allow me to exclude properties really easily.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3.  
    4. using System;
    5. using System.Reflection;
    6. using System.Collections.Generic;
    7.  
    8. using Newtonsoft.Json;
    9. using Newtonsoft.Json.Serialization;
    10. using Newtonsoft.Json.Utilities;
    11.  
    12. public class ExcludeContractResolver : DefaultContractResolver {
    13.     List<string> _properties;
    14.  
    15.     public ExcludeContractResolver( List<string> properties ) {
    16.         _properties = properties;
    17.     }
    18.  
    19.     protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization ) {
    20.         JsonProperty jsonProperty = base.CreateProperty( member, memberSerialization );
    21.  
    22.         if ( _properties.Contains( jsonProperty.PropertyName ) ) {
    23.             jsonProperty.Ignored = true;
    24.         }
    25.        
    26.         return jsonProperty;
    27.     }
    28. }
    29.  
    Example usage as follows.

    Code (csharp):
    1.  
    2. List<string> properties = new List<string>();
    3.  
    4. properties.Add( "customPreviewImage" );
    5.  
    6. JsonSerializerSettings settings = new JsonSerializerSettings();
    7.  
    8. settings.ContractResolver = new ExcludeContractResolver( properties );
    9.  
    10. string json = JsonConvert.SerializeObject( targetObject, Formatting.None, settings );
    11.  
    Now I just supply it a list of properties that I want to ignore and tada! I wonder though, how do you use multiple contract resolvers? I want to resolve Texture2D to the textures name, but I think that also takes a custom contract resolver or will it do that automatically?

    Also I receive the below, but I'm not sure what it means. It seams to happen when "name" property is added.

    Code (csharp):
    1.  
    2. NullReferenceException
    3. (wrapper dynamic-method) UnityEngine.Object.Getname (object) <IL 0x00006, 0x00046>
    4. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (object) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:102)
    5. Rethrow as JsonSerializationException: Error getting value from 'name' on 'UnityEngine.Texture2D'.
    6. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (System.Object target) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:106)
    7. Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContract collectionValueContract) (at Assets/JsonDotNet/Source/Serialization/JsonSerializerInternalWriter.cs:332)
    8.  
    It seams to point to Texture2D, but I don't quite understand why it'd error on getting a Texture2D name.

    It looks like with jsonProperty I can do jsonProperty.Converter and testing the jsonProperty.PropertyType to see if it's Texture2D and somehow have a custom converter for Texture2D there. What do you think? I saw the example of Texture2D converter awhile back, but I don't quite understand it.
     
    Last edited: Feb 16, 2014
  30. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Ok I'll take a look. I'm not sure why it would throw an exception either unless your Texture2D was null for some reason.
     
  31. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Dustin!

    Thanks for the support!

    Here is a small exampe scene! https://www.dropbox.com/s/7gcih12l3kfzf8f/JsonSaveReloadExample.zip

    I want to save all status from the objects from the scene and reload it on a later time. I can have an infinite number of these prefabs instanciated in runtime and I need to recover all of them!

    I will need for something a lot more complex, but with this small sample I should be able to get everything I will need!

    Thanks a lot
     
  32. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    I think the Texture2D is null and that's the issue. Is there a way to tell it to just return null for "name" instead of trying to access a property of a null Texture2D? Maybe I need to write a custom converter and specify it in my custom contract resolver to be used for Texture2D? Even better would be for it to instantiate a new Texture2D then the properties should work fine.
     
  33. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Well... Lets start with something simpler! =)

    I'm trying to save the current status of a class to the memory and later reload it's status back :

    Code (csharp):
    1.  
    2.     public void Save()
    3.     {
    4.         using (saveData = new MemoryStream())
    5.         {
    6.             var serializer = new JsonSerializer();
    7.             var writer = new BsonWriter(saveData);
    8.             serializer.Serialize(writer, player);
    9.  
    10.             Debug.Log(BitConverter.ToString(saveData.ToArray()));
    11.         }
    12.        
    13.     }
    14.  
    15.     public void Load()
    16.     {
    17.         using (saveData)
    18.         {
    19.             var serializer = new JsonSerializer();
    20.             var writer = new BsonWriter(saveData);
    21.             serializer.Serialize(writer, this);
    22.  
    23.             //Reset the stream back to the beginning
    24.             saveData.Seek(0, SeekOrigin.Begin);
    25.  
    26.             //Create the BSON Reader and Deserialize
    27.             var reader = new BsonReader(saveData);
    28.             serializer.Deserialize<Player>(reader);
    29.         }
    30.     }
    31.  
    Am I getting close? =)

    Two more questions :
    - It saves all my public properties and [SerializeField]?
    - And how to save it to a file and reload it back?

    Thanks!
     
    Last edited: Feb 19, 2014
  34. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Well... you'll need to just check whether the Texture2D is null before you try to access its properties if you're writing your own resolver. You technically could create an "empty" Texture2D but it's a bit more work because Texture2D doesn't have a public parameterless constructor. I'd have to see your whole code... not sure how you were trying to access the name property...
     
  35. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Ok... you're close except that the memory stream is an unmanaged resource so you don't want to leave it lying around. Instead, write that stream to a byte array and just keep that byte array variable stored somewhere.

    As for writing to a file, you can just use System.IO and create a file in the persistantDataPath and write it out to the file using File.WriteAllBytes and passing the bson byte array to it.
     
  36. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    My custom contract resolver is in my above post. It just checks if the propertyName is in the list of properties to ignore. If it is then it sets its ignore flag. I don't do anything with the properties beyond that. Texture2D contains "name" which is the name of the texture, but if the Texture2D is null that'll error and creating an empty Texture2D won't work as you said it has no public parameterless constructor. I tried setting the null handling in JsonSerializerSettings to ignore null, but it didn't seam to do anything.
     
  37. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Ok... could you show me the object you're trying to serialize? I took another look at your exception... I should have looked at it a little harder the first time but I made a bad assumption based on your statement about the Texture2D. :)

    Code (csharp):
    1.  
    2. NullReferenceException
    3. (wrapper dynamic-method) UnityEngine.Object.Getname (object) <IL 0x00006, 0x00046>
    4. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (object) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:102)
    5. Rethrow as JsonSerializationException: Error getting value from 'name' on 'UnityEngine.Texture2D'.
    6. Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (System.Object target) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:106)
    7. Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonObjectContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContract collectionValueContract) (at Assets/JsonDotNet/Source/Serialization/JsonSerializerInternalWriter.cs:332)
    8.  
    [/QUOTE]

    The problem here is that whatever property that Texture2D belongs to isn't actually "null" so it's trying to serialize it. However, when it tries to access the "name" property, the UnityEngine.Object.Getname function is throwing a NullReferenceException. I'm not sure why it's throwing that exception, but it's not the serializer that is the problem. If you tried yourself to get that... like:

    Code (csharp):
    1.  
    2. var texName = yourObject.yourTexture2D.name;
    3.  
    That would likely throw the same exception... If I could have a look at what you're actually trying to serialize that would help a ton.
     
  38. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Thanks for the info, now I can save basic classes! =)

    Now... I have a number of runtime instanciated prefabs, it's all different prefabs, but all of them have a Component called BaseSprite which contains all the information important for saving.

    But, how can I save a list of these gameObjects, save all info from the BaseSprite of each one, and later recreate them in runtime, loading back all relevant information?

    Cezar
     
  39. EmeralLotus

    EmeralLotus

    Joined:
    Aug 10, 2012
    Posts:
    1,459
    I have the same exact use case. Dustin, once this situation is resolved. Would you consider a request to make this example available for us to follow?
    It will be super helpful.

    Cheers.
     
  40. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yeah, I'm on conference this afternoon and it's my anniversary so I probably won't look at it until tomorrow. :) But basically it's going to take a little work on your part. We can't directly serialize / deserialize a gameObject with JSON .NET... because there are some oddities to how Unity handles it. Some of the properties like "rigidBody" aren't really null... so the serializer will try to serialize them, but then Unity will chuck a wobbly and throw it's own custom exception saying you tried to access the properties but it hasn't been initialized. There are several that do this.

    Now, one thing you can do is create an additional component that keeps track of the prefab name, location, whatever info you need... Then you can tag all of these prefabs with a specific tag. You can get all of the gameObjects by tag name... create list of some type of object like "SaveInfo" that has properties for all the prefab info you need... then for each gameObject that is returned, find your custom component with all that info, and execute a method that returns an object with all of that information. Then just serialize the list.

    Now when you need to restore it, deserialize that list and based in the info saved, loop through, reinstantiate all of the prefabs and reset all of that information (such as your transform, your BaseSprite, etc).
     
  41. tbj22

    tbj22

    Joined:
    Sep 18, 2013
    Posts:
    15


    Thanks for the great work on this plugin. I bought it due to the fantastic commitment to support you have shown on this thread.

    I have a question: How do I use the "EnumValueConverter" option?

    The snippet referenced in your blog post:
    JsonSerializerSettings.DefaultEnumSerializationHandling = EnumSerializationHandling.Value;

    Doesn't seem to exist.

    I have the latest version from the asset store.

    Thanks,

    Travis
     
  42. tbj22

    tbj22

    Joined:
    Sep 18, 2013
    Posts:
    15
    To partially answer my own question:

    This is what I did
    Code (csharp):
    1.     [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
    2.     public enum ButtonState
    3.     {
    4.         Enabled,
    5.         Disabled,
    6.     }
    I'm still curious if there are other approaches.
     
  43. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh man... I need to fix that blog entry, thanks for reminding me and thanks for your purchase! What you're doing is correct. If you import Newtonsoft.Json.Converters then you only have to use:

    Code (csharp):
    1.  
    2. [JsonConverter(typeof(StringEnumConverter))]
    3.  
    That's the best way to do it, however, you don't always have access to add attributes because it could be a class in a DLL (or a built in class). In that case, there is an overload for the SerializeObject method that allows you to pass an array of Converters:

    http://james.newtonking.com/json/help/?topic=html/M_Newtonsoft_Json_JsonConvert_SerializeObject_2.htm
     
  44. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    I'm trying to serialize an array of the below.

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public sealed class RtsRuntimeCategoryRecord {
    4.  
    5.     [SerializeField]
    6.     private string name;
    7.  
    8.     [SerializeField]
    9.     private int id;
    10.  
    11.     [SerializeField]
    12.     private RtsRuntimeBrushRecord[] brushes;
    13.  
    14.     public string Name {
    15.         get { return name; }
    16.     }
    17.  
    18.     public int Id {
    19.         get { return id; }
    20.     }
    21.  
    22.     public RtsRuntimeBrushRecord[] Brushes {
    23.         get { return brushes; }
    24.     }  
    25.  
    26.     public RtsRuntimeCategoryRecord( string Name, int Id, RtsRuntimeBrushRecord[] Brushes ) {
    27.         name = Name;
    28.         id = Id;
    29.         brushes = Brushes;
    30.     }  
    31. }
    32.  
    RtsRuntimeBrushRecord is as follows

    Code (csharp):
    1.    
    2. [System.Serializable]
    3. public sealed class RtsRuntimeBrushRecord {
    4.  
    5.     [SerializeField]
    6.     private string name;
    7.  
    8.     [SerializeField]
    9.     private Brush brush;
    10.  
    11.     [SerializeField]
    12.     private Texture2D preview;
    13.  
    14.     public string Name {
    15.         get { return name; }
    16.     }
    17.  
    18.     public Brush Brush {
    19.         get { return brush; }
    20.     }
    21.  
    22.     public Texture2D Preview {
    23.         get { return preview; }
    24.     }  
    25.  
    26.     public RtsRuntimeBrushRecord( string Name, Brush Brush, Texture2D Preview ) {
    27.         name = Name;
    28.         brush = Brush;
    29.         preview = Preview;
    30.  
    31.     }  
    32. }
    33.  
    I extended my contract resolver with a Filter feature so I can filter to a specific list of property names. I have it only accepting "name". The issue comes when it's trying to serialize preview, which is either going to be a Texture2D or null. The preview comes from the below object and its Preview property.

    http://rotorz.com/tilesystem/api?ref=A75BC8A

    If Preview property is empty it trys to get it from http://rotorz.com/tilesystem/api?ref=177EB580.

    If I exclude the preview property from serializing then I have no issues. So somehow it's going bonkers when trying to serialize the Texture2D. Maybe it's worth noting that the Texture2D in some of these cases are .asset and not .png for example; is that a problem?

    I output the preview.name directly in code and it works fine as long as it's not null otherwise I get an obvious null exception. The error seams like to me it's trying to access name for a null Texture2D property as it's throwing a NullReferenceException.
     
    Last edited: Feb 20, 2014
  45. Krileon

    Krileon

    Joined:
    Oct 30, 2012
    Posts:
    642
    Ok, I used your Texture2D converter example on page 9 and then did a Debug.Log( texture ) before writer.WriteValue(texture.name); and that's exactly what the issue is. It's trying to do texture.name when the texture is null.

    To fix it all I had to do was add ( texture ? texture.name : null ) to the custom converter. Is there no way for me to have this built into all the converters? It shouldn't try to access a property that doesn't exist if it the object is null.
     
    Last edited: Feb 20, 2014
  46. iplay88keys

    iplay88keys

    Joined:
    Feb 24, 2014
    Posts:
    1
    I am running into the following error just after importing the files into my Unity 4.3 project:

    Assets/JsonDotNet/Source/Converters/IsoDateTimeConverter.cs(98,20): error CS0117: `Nullable' does not contain a definition for `GetUnderlyingType'

    any help would be appreciated.

    Thanks!

    Edit: Figured it out. I did a search of this forum and nothing came up earlier, but strangely enough, doing a recent search led me to a post about how Soomla used their own implementation of the Nullable class which caused conflicts.
     
    Last edited: Feb 24, 2014
  47. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    I was able to Serializate a List<BoughtItem>!

    But I was not able to Deserialzate it, how can I do this?

    JsonSerializationException: Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[BoughtItem]'.

    Code (csharp):
    1.  
    2. List<BoughtItem> listToLoad = serializer.Deserialize<List<BoughtItem>>(reader);
    3.  
    4. Any advice?
     
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yep, glad you found it. I've actually contacted the Soomla folks about it but I get no reply from them. I may try again because my asset may not be the only thing that breaks as a result of them overriding built in .NET types.

     
  49. sevensails

    sevensails

    Joined:
    Aug 22, 2013
    Posts:
    483
    Dustin... here is my full code, please take a Look!


    What I'm trying to achieve :

    I have a scene with lot of objects, and these objects have a component called CSprite. I want to save a list of these component of scene, recreate all of them, and reload the info in it's CSprite component.

    I search all the gameObjects on scene I want to save, and create an BoughtItem of item, here I have it's prefab name, and the serialization of it's Csprite component. And add it to ListBoughtItem.

    And I need to serializate this list, and save it...

    Here is how I'm trying to do this!

    Any advice?


    Code (csharp):
    1.  
    2. [Serializable]
    3. public class ListBoughtItem : List<BoughtItem>
    4. {
    5.    
    6. }
    7.  
    8. public class BoughtItem
    9. {
    10.     public string prefab_name;
    11.     public Byte[] _saveData;
    12.  
    13.     public BoughtItem(JsonSerializer serializer, GameObject _objecttosave)
    14.     {
    15.         prefab_name = _objecttosave.GetComponent<BaseSprite>().MyPrefabName;
    16.  
    17.         using (MemoryStream tempSaveData = new MemoryStream())
    18.         {
    19.             var writer = new BsonWriter(tempSaveData);
    20.             serializer.Serialize(writer, _objecttosave.GetComponent<CSprite>().SaveData);
    21.  
    22.             _saveData = tempSaveData.ToArray();
    23.         }
    24.     }
    25.  
    26.     public CSprite CSprite()
    27.     {
    28.         using (MemoryStream saveData = new MemoryStream(_saveData))
    29.         {
    30.             var serializer = new JsonSerializer();
    31.             saveData.Seek(0, SeekOrigin.Begin);
    32.  
    33.             //Create the BSON Reader and Deserialize
    34.             var reader = new BsonReader(saveData);
    35.             return serializer.Deserialize<CSprite>(reader);
    36.         }
    37.     }
    38. }
    39.  
    My code to save the list

    Code (csharp):
    1.  
    2.   public void Save()
    3.     {
    4.         var listToSave = new ListBoughtItem();
    5.         var serializer = new JsonSerializer();
    6.  
    7.         var objects = GameObject.FindGameObjectsWithTag("BoughtItems");
    8.         foreach (var objecttosave in objects)
    9.             listToSave.Add(new BoughtItem(serializer, objecttosave));
    10.  
    11.  
    12.         using (MemoryStream gardenStream = new MemoryStream())
    13.         {
    14.             var writer = new BsonWriter(gardenStream);
    15.             serializer.Serialize(writer, listToSave);
    16.             File.WriteAllBytes(Application.persistentDataPath + "/garden.sav", gardenStream.ToArray());
    17.            
    18.         }
    19.     }
    20.  
    My code to Load :

    Code (csharp):
    1.  
    2.   public void Load()
    3.     {
    4.         var listToSave = new ListBoughtItem();
    5.  
    6.         if (File.Exists(Application.persistentDataPath + "/garden.sav"))
    7.         {
    8.             var _saveData = File.ReadAllBytes(Application.persistentDataPath + "/garden.sav");
    9.  
    10.             using (MemoryStream saveData = new MemoryStream(_saveData))
    11.             {
    12.                 var serializer = new JsonSerializer();
    13.                 saveData.Seek(0, SeekOrigin.Begin);
    14.  
    15.                 //Create the BSON Reader and Deserialize
    16.                 var reader = new BsonReader(saveData);
    17.                 listToSave = serializer.Deserialize<ListBoughtItem>(reader);
    18.  
    19.                 foreach (var boughtItem in listToSave)
    20.                 {
    21.                     GameObject gameObject =
    22.                         Instantiate(Resources.Load("prefab_" + boughtItem.prefab_name)) as GameObject;
    23.                     CSprite tempSprite = gameObject.GetComponent<CSprite>();
    24.                     tempSprite = boughtItem.CSprite();
    25.                 }
    26.             }
    27.         }
    28.     }
    29. }
    30.  
     
  50. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Ok... I'm going to have to figure out what you've got going on here....

    So a couple of things. You don't need the Serializable attribute. You can remove that. What I'm trying to figure out is your CsSprite method in your BoughtItem that serializes the sprite and returns it.... Also, you're passing the serializer into your BoughtItem. What you should do instead is just create a list of BoughtItem with each having a property of CsSprite. You can then serialize and deserialize that whole list... there's no need to be deserializing each CsSprite individually... You also don't need ListBoughtItem to be a class. You can just create a List<BoughtItem> instead of having a wrapper class since your wrapper isn't doing anything extra.

    Actually could you do me a favor? Could you export your project to a unitypackage and send me a PM with a link to download? Or trim it down so it just has this repro code in it... I want to step through with the debugger and see what's going on... I can probably rewrite it to give you a cleaner implementation so you're not passing that serializer around. It's a lot to digest without being able to debug it.