Search Unity

JSON .NET for Unity

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

  1. TedAtPlotagon

    TedAtPlotagon

    Joined:
    Aug 6, 2013
    Posts:
    10
    When I try to build to Windows App Store using Unity 4.5 and JSON.Net for Unity v1.3.5 I get compile errors. Anyone know how to resolve this?

    Assets/External Tools/JsonDotNet/Source/WinRT/RT_JsonWriter.cs(30,14): error CS0234: The type or namespace name `Numerics' does not exist in the namespace `System'. Are you missing an assembly reference?

    Assets/External Tools/JsonDotNet/Source/WinRT/Linq/RT_JToken.cs(29,14): error CS0234: The type or namespace name `Dynamic' does not exist in the namespace `System'. Are you missing an assembly reference?

    Assets/External Tools/JsonDotNet/Source/WinRT/Linq/RT_JToken.cs(32,14): error CS0234: The type or namespace name `Numerics' does not exist in the namespace `System'. Are you missing an assembly reference?
     
  2. noanoa

    noanoa

    Joined:
    Apr 17, 2014
    Posts:
    225
    Hello. How do you use OnSerializing attribute and other callbacks with Json-Net? I tried using System.Runtime.Serialization.OnSerializingAttribute but Unity don't have it...
     
  3. dkely

    dkely

    Joined:
    Mar 3, 2014
    Posts:
    22
    Hi Dustin,
    Just purchased this and it looks great but it is not quite working. I get the following:

    JsonSerializationException: Could not load assembly 'Database_v2'.

    'Database_v2' is the namespace for the objects i am trying to deserialize into although the DLL's name is not 'Database_v2' but 'DTO'.
    I thought that might cause an issue so i changed it to match the namespace. This caused the editor to crash with no error and a Unity bug reporter to pop up - perhaps similar to the issue you suggest in the quoted comment? I do have PreserveReferencesHandling on and set to Objects.

    I also realized i had not included another DLL. It used reflection and i wanted to quickly test it so i changed the target to standalone but i am still get the original error (now i have changed back to the old DLL name to stop the crashing).

    I have tried deserialization on the server to make sure it is definitely a Unity issue and it is working fine there.
    Directly before use i can grab the strings contents and use it with something like at http://www.jsoneditoronline.org/ and it will work fine with everything seemingly in place. Any idea what could be the issue?
     
  4. yannru.cheng

    yannru.cheng

    Joined:
    Jan 23, 2013
    Posts:
    1
    Is Serialization Attributes supported?

    thanks
     
  5. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I'll have to look... it may depend on what platform you're targeting. I'm on vacation this week so I have limited access to a computer but I'll be able to look at it on Monday. I know it can be used because others have used it but I haven't tried it myself.
     
  6. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yes, serialization attributes are supported but not in the full capacity. There are a few I believe that aren't honored by JSON .NET, but it has it's own equivalent attributes such as [JsonIgnore]. There are also attributes for setting the serialization to OptIn where only the members you specify to be serialized will be serialized.
     
  7. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Is this happening during Deserialization? What platform are you targeting? If this is for Android I have a patch for you that will be part of the next release where occasionally it would throw that exception. I'll PM you with my Skype contact and I can get you the patch.

    As for the Unity editor crash, I have seen this as well. Sometimes with PreserveReferencesHandling set to on, the Unity editor just closes for now apparent reason. This also happens when using serialization callbacks for an editor extension / GUI element. When it uses reflection to resolve the callbacks the editor just closes. I'm working on creating a repro for that so I can file a bug report but I need to test it on 4.5 (and 4.6 Beta) to see if it has been fixed.

    I'm also working on Ted's issue from above which we discovered was caused by setting the compilation option to None instead of Use .NET Core. Evidently there are some .NET 4+ types and namespaces that aren't included if you're not using .NET core so I am in the process of adding directives to work around those but as long as you don't set the option to None then everything works fine for Windows Store Apps.
     
  8. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    Dustin, great plugin. Having some trouble on the Windows App Store build with Unity 4.5.1f on build.

    Assets\JsonDotNet\Source\WinRT\Serialization\RT_DefaultContractResolver.cs(113,13): error CS0104: 'BindingFlags' is an ambiguous reference between 'System.Reflection.BindingFlags' and 'Newtonsoft.Json.Utilities.BindingFlags'

    I searched the forum to see if this was a known issue, but came up blanks. Do you know if this will be corrected in the updated version you are planning for 4.5?

    Thanks.
     
  9. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yes, there is a patch to fix the BindingFlags issue. BindingFlags wasn't available in Windows Store Apps so JSON .NET creates its own to work around it. Unity shimmed in BindingFlags so now it doesn't know which one to use. Here is a patch that will fix the BindingFlags issue on Windows Store Apps... Install the asset, then import the attached unity package and it will replace the necessary files.
     

    Attached Files:

  10. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    Hate to be the bringer of bad new. Applied the patch, now on build get the following:

    Assets\JsonDotNet\Source\WinRT\RT_JsonConvert.cs(622,19): error CS0117: 'Task' does not contain a definition for 'Factory'

    Assets\JsonDotNet\Source\WinRT\RT_JsonConvert.cs(805,19): error CS0117: 'Task' does not contain a definition for 'Factory'

    Assets\JsonDotNet\Source\WinRT\RT_JsonConvert.cs(836,19): error CS0117: 'Task' does not contain a definition for 'Factory'

    Assets\JsonDotNet\Source\WinRT\RT_JsonConvert.cs(887,19): error CS0117: 'Task' does not contain a definition for 'Factory'
     
  11. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yeah, you'll have to make a change to your compilation options. Unity appears to have added an option to not use the .NET Core when building for Windows Store Apps. I don't remember off the top of my head what that option is called, but I think it is set to None which is why you're getting the other errors. If you change it to Use .NET Core then it will work. I'm in the process of hunting down and applying directives so it will work with the new "None" option.
     
  12. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    is that the NETFX_CORE directive you are thinking of?
     
  13. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    my app is set to use Core, and the errors persist.
     
  14. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Hmm... are you targeting 8.0 or 8.1 SDK? I just built a test project using 8.1 and Use .NET Core and it works fine. I'm on 4.5.0 though... I'm going to double check with 4.5.1 and see if Unity broke something. :/
    FYI, it's compilation override in the player settings I was talking about:


    UseNetCore.PNG
     
  15. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh you know what... that causes issues with Numerics and Dynamic (when you use None). You're issue is with Task and I can tell you what the problem is. Someone has created a class called Task and put it in the global namespace (they didn't put it in a namespace). So, since System.Task is a built in type in .NET 4+ (which is what Windows Store Apps are using), the library is probably trying to use the Task class that's in your project which doesn't have a property called Factory. In order to fix it you're going to have to find where Task is declared in your project (just go to the source of the error in Visual Studio, the line that it's being thrown in my asset, then click on Task and hit F12 which will take you to where it's declared). You'll have to put the "Task" class in your project in a namespace.

    This is one of the big drawbacks of a lot of things in Unity not using Namespaces. People pollute the global namespace with classes that may already exist in the full framework and it causes things to break. I've seen this happen with another asset that conflicts with mine because they decided to create a class called Nullable which blows up System.Nullable<T>. For instance, when you say "int?" it's just an alias that shortcuts to Nullable<int>.
     
  16. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    thanks, I will look for that. I will let you know what I find
     
  17. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    haha

    found it in 2 seconds. my own app has a class named Task.

    Doh!
     
  18. phocker

    phocker

    Joined:
    Sep 12, 2010
    Posts:
    57
    ok - task errors no longer there - will let you know how actual build in VS goes - thanks for all your help this morning - great customer service
     
  19. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    you're very welcome
    glad you found the culprit :)
     
  20. noanoa

    noanoa

    Joined:
    Apr 17, 2014
    Posts:
    225
    The forum didn't give me alerts for the replies on this thread for some reason -.-
    Thanks for your reply. Using System.Runtime.Serialization, [OnSerializing] attribute worked perfectly with Json-Net and I assume other callbacks will also work alike :)

    [Edit] I just noticed I was using "System.Runtime.Serialization.OnSerializingAttribute" in the last post instead of just "System.Runtime.Serialization". Embarrassing mistake, oh well.
     
    Last edited: Jun 16, 2014
  21. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    good good. Yeah it seems the forum doesn't do per thread alerts the samd way the old one does. It sends me an alert for one thread and then no more alerts for any other threads until I've visited the forum.
     
    noanoa likes this.
  22. dkely

    dkely

    Joined:
    Mar 3, 2014
    Posts:
    22
    Thanks for the reply. I actually solved this myself although i am not sure how... I have been experiencing lots of issues with my dll's lately (see post http://forum.unity3d.com/threads/web-build-compiler-error.192090/) .
    This seemed unrelated as it only occurred during the parse but now it works unless i switch to web (despite the fact i did actually use stand alone when i posted this issue originally!) Anyway think its something weird on my side not yours. Thanks for the great tool!
     
  23. wx3labs

    wx3labs

    Joined:
    Apr 5, 2014
    Posts:
    77
    Can JSON.Net be used to serialize/deserialize Unity components/gameobjects? It seems like if I try to serialize a class that inherits from Monobehavior or Unity's Object I run into errors like missing rigidbody, etc.

    I'm currently using your JSON.Net tool to pull in some externally defined game data.

    What I'd like to be able to do is use JSON to store certain objects and their inspector defined variables. For example:
    • Start with a scene with a bunch of stars, planets and other prefab instances
    • Tweak the various instance component values through the inspector
    • Turn all that data into JSON
    • Then deserialize that JSON back into the original scene
    Without being able to serialize Unity components, this seems tricky. Am I missing something obvious?
     
  24. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    It can, but it requires some extra work. You'll need to use custom type converters for your types and manually do checks against some properties like RigidBody. The problem is the way Unity handles "null" for those objects. It overrides the == operator and fakes null. So, the JSON Serializer doesn't know this and it sees that the rigidbody property is not really null and thus tries to serialize it.

    So, in fact, the rigidbody exists but when JSON .NET attempts to serialize that object, Unity throws its own custom error about trying to access properties of a component that haven't been added to your gameobject.

    Now, serializing your gameobjects directly might not be the best way to go about it anyway because you deserialize and directly add components to your gameobject so it doesn't do you any good to deserialize a component because you can't re-add it anyway.

    Your best bet is to create classes or structs within your game objects that hold the crucial data and serialize those, or you can create "proxy" classes which have all of the same properties you need from your game object, copy the data to the proxy and serialize it as long as it doesn't inherit from Monobehavior or GameObject. Or you can create a custom type converter that will manually check rigidbody (or even ignore it). There are some other properties that will give you issues as well but I can't remember what they are off the top of my head.
     
    oferei likes this.
  25. wx3labs

    wx3labs

    Joined:
    Apr 5, 2014
    Posts:
    77
    Thanks for the response, I've already gone down the route of creating data objects to hold the persistent game data. I think I've got a viable system-- the "magic sauce" I was missing was I didn't know about the TypeNameHandling flag, which lets me have different data objects for different object types and still put everything into a generic list.
     
  26. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    In my opinion that's the best way to go because you can create a container class and add all of your data bits to it, then serialize and deserialize one thing if you want (or different containers for groups). Then you can centralize data by types (for instance, playerdata could be in one file, worlddata could all be in another). And yeah, TypeNameHandling.All is what you want in your case. It's called Polymorphism. If you look in my examples that are provided with the asset you'll even find a polymorphism example that demonstrates exactly what you're doing with using a base / inherited types in a single list.
     
  27. Ghosthowl

    Ghosthowl

    Joined:
    Feb 2, 2014
    Posts:
    228
    Hi Dustin,

    I know all developers hate getting this question... but I have to ask: are there any plans to update this package? Just wondering as some features of Version 6.0.3 (I think they were introduced in version 6.0) are missing and when I was originally getting this to work i was unable to use said features. I eventually found ways around them, just a little bit more work on my part. Just curious.

    Thanks!
     
  28. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I do plan to update but the scope of that project is very large and I wont do so until Unity 5 is released so I can work on compatibility across versions. Which features were you needing?
     
  29. kev_pearman

    kev_pearman

    Joined:
    Jul 2, 2014
    Posts:
    5
    Dustin, i recently purchased your json.net asset and i'm having an issue deserlalizing a list of objects.
    If i have a class defined in my project called MyClass and i want to deserialze a collection of MyClass objects into a list i use the following

    JsonConvert.DeserializeObject<List<MyClass>>(jsonString);

    This is fine in the editor but throws
    ExecutionEngineException: Attempting to JIT compile method '(wrapper dynamic-method) System.Collections.Generic.List`1<MyClass>:CreateSystem.Collections.Generic.List`1[[MyClass, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] ()' while running with --aot-only.

    When used on my iPhone.

    Any advice?
     
  30. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    This can still happen in some cases because of the way the AOT compiler works. If you have never created an instance of your object before but try to deserialize it, the AOT compiler doesn't know how to build the object from the constructor using reflection. I'm guessing it's specifically your class and not the list. Try the following right before you deserialize:

    Code (csharp):
    1.  
    2. var testInstance = new MyClass();
    3. JsonConvert.DeserializeObject<List<MyClass>>(jsonString);
    4.  
    If you still get the error try the following:
    Code (csharp):
    1.  
    2. var testInstance = new MyClass();
    3. var testList = new List<MyClass>();
    4. JsonConvert.DeserializeObject<List<MyClass>>(jsonString);
    5.  
    I'm betting that your AOT error will go away as soon as you do that because once you've constructed an instance of your class the compiler will know how to construct it. Now, the above is just a way to identify whether this is the issue, so if this works you'll want to move the code that creates your class and list somewhere else.

    What I recommend is creating a static class to initialize your objects. Then, have a game object that is created before any of your deserialization happens, or just call it before you deserialize because we're only going to instantiate the stuff once ever, that way you're not constantly allocating. So your class might look like the following:

    Code (csharp):
    1.  
    2. public static class ObjectInitializer
    3. {
    4.     private static bool IsInitialized = false;
    5.  
    6.     public static void Initialize()
    7.     {
    8.           if(IsInitialized)
    9.               return;
    10.  
    11.           IsInitialized = true;
    12.  
    13.           new MyClass();
    14.           new List<MyClass>();
    15.     }
    16. }
    17.  
    Note that I didn't assign MyClass or the list to a variable. This is so it will immediately go away. No need to store those objects, we just need to create them once from the AOT compiler. In fact, you probably don't need the new List<MyClass> bit, so only add it depending on what you find out from the tests above. Then, somewhere in your game before you ever do any deserialization for the first time just do the following:

    Code (csharp):
    1.  
    2. ObjectInitializer.Initialize();
    3.  
    It's best to do that in the Start() function of some game object so you only ever call it once. Then in your Initialize method of your static class you just add any classes that are giving you problems with AOT.

    If this doesn't fix your issue, drop me an email. My email address is in the readme.txt file with the asset, or you can just contact me through my blog (link in my signature) and I can get with you on Skype to work it out.
     
  31. AndyLL

    AndyLL

    Joined:
    Aug 25, 2013
    Posts:
    75
    Stupid JSON question.

    Json string returned from the server:
    {"PlayerDataOutResult":{"BestWord":"QUIP","BestWordScore":15,"BiggestLossStreak":36,"BiggestWonStreak":6,...}}

    Class being Deserialized into:

    public class UserLevelData
    {
    public int Index { get; set; }
    public int Played { get; set; }
    public int Won { get; set; }
    public int Loss { get; set; }
    public int Quit { get; set; }
    public int HighScore { get; set; }
    public string BestWord { get; set; }
    public int BestWordScore { get; set; }
    ...
    }

    Statement:
    UserLevelData uld = JsonConvert.DeserializeObject<UserLevelData>(request.text);

    This does not work. The resulting class is blank.

    However... if I remove the {"PlayerDataOutResult": wrapper from my data it deserializes correctly.
    {"BestWord":"QUIP","BestWordScore":15,"BiggestLossStreak":36,"BiggestWonStreak":6,...}

    What am I doing wrong?
     
  32. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    your json string represents an object with a PlayerDataOutResult property. That's why removing the wrapper works. Instead, you could create this class:

    Code (csharp):
    1.  
    2. public class UserDataResult
    3. {
    4. . public UserLevelData PlayerDataOutResult { get; set; }
    5. }
    6.  
    then do

    UserLevelData uld = JsonConvert.DeserializeObject<UserLevelData>(request.text);

    UserDataResult udr = JsonConvert.DeserializeObject<UserDataResult>(request.text);

    That will properly deserialize your string and then to get your level data it's just:
    udr.PlayerDataOutResult.
     
  33. kev_pearman

    kev_pearman

    Joined:
    Jul 2, 2014
    Posts:
    5
    Dustin,
    thanks for the reply, based on other articles i've read around AOT compile errors and generics that does seem to make sense. Unfortunately i don't have my mac with me today so i can't test it but i will give that a go tomorrow and let you know if i have any more problems.

    Cheers


    Kev
     
  34. waxx

    waxx

    Joined:
    Apr 1, 2013
    Posts:
    48
    Hey man bought your json package and so far it's been a blast but I ran into a problem with following objects:

    List<Pair<string, object[]>>

    where Pair is
    Code (csharp):
    1.  
    2. public class Pair<T, U>
    3. {
    4.   public Pair()
    5.   {
    6.   }
    7.  
    8.   public Pair(T first, U second)
    9.   {
    10.   this.First = first;
    11.   this.Second = second;
    12.   }
    13.  
    14.   public T First { get; set; }
    15.   public U Second { get; set; }
    16. };
    17.  
    Strings to First are loaded fine but serializing object[] where objecs inside are floats/integers does not work. Any way around this?
     
  35. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    @wax -
    Is it s serialization or Deserialization that's giving you the problem? I'm guessing it's the latter. If you look at your JSON string it should be serialized properly. The problem you're likely running into is that the target type to deserialize into is object[] so it has no idea what the data types should be to stuff into it. What you'll want to do is change the way you serialize and deserialize a bit for those.

    Create a new instance of JsonSerializerSettings and set

    Code (csharp):
    1. settings.TypeNameHandling = TypeNameHandling.All;
    That will store the type name for the floats/integers along with the serialized data. You'll need to pass that settings instance into the SerializeObject and DeserializeObject methods as well. There are overloads for it. Give that a try. If that doesn't work send me a PM and we'll hook up on Skype to see what's going on with it.
     
  36. AndyLL

    AndyLL

    Joined:
    Aug 25, 2013
    Posts:
    75
    Thanks... that did it. I was trying to inherent off the container which wasn't working.
     
  37. Fuzzcode

    Fuzzcode

    Joined:
    Dec 5, 2013
    Posts:
    10
    Hey Dustin,

    I think I've found a bug with the JObject Remove functionality. I have the following code in my game

    Code (CSharp):
    1. var data = JObject.Parse(PlayerPrefs.GetString(SaveController.Instance.SavePath));
    2.  
    3. //unimportant code
    4.  
    5. if (data["singleData"] != null)
    6. {
    7.     Debug.Log(data.ToString());
    8.     data.Remove("singleData");
    9. }
    10.  
    11. //unimportant code  
    12.  
    13. Debug.Log(data.ToString());
    14. data.Add("singleData", temp);

    When my data does not have the singleData property everything works fine. When it does have it however the data.Add throws this error


    Using the debug logs I have checked that my json data does not contain the singleData property when I try to add. This code also used to work when I was using the dll but I switched to your solution so I could port the game to the web browser.

    I can work around this but if you have a solution it would be appreciated!
     
  38. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    That error shouldn't be thrown unless singleData already exists. Could you do me a favor.. add:

    Code (csharp):
    1.  
    2. Debug.Log(PlayerPrefs.GetString(SaveController.Instance.SavePath));
    3.  
    I want to see what the raw JSON string looks like.
     
  39. Fuzzcode

    Fuzzcode

    Joined:
    Dec 5, 2013
    Posts:
    10
    It looks like

     
  40. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh, I misunderstood something. You want to remove the singleData property and re-add it. And I notice you mentioned web player so I think I know what's going on. The Web Player build of Unity had some big differences in the implementation of Mono's KeyedCollection, including a missing constructor amongst other things, so I did a fair amount of work to code around them. I may have missed something that is causing the Remove functionality to not operate on the underlying collection properly so let me take a look at it. I have to leave for just a bit but I'll take a look when I get home and see if I can get it patched up quickly. If you have Skype, send me a PM with your Skype contact details.
     
  41. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    The above problem was again due to the KeyedCollection differences in the WebPlayer implementation. We were able to put in a workaround to fix the problem and it will be in the forthcoming 1.3.6 release.
     
  42. thylaxene

    thylaxene

    Joined:
    Oct 10, 2005
    Posts:
    716
    Anyone seen this issue and any idea how to fix?

    StackOverflowException
    UnityEngine.Color.get_linear () <IL 0x00006, 0x0002a>
    (wrapper dynamic-method) UnityEngine.Color.Getlinear (object) <IL 0x00006, 0x00047>
    Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue (object) (at Assets/JsonDotNet/Source/Serialization/DynamicValueProvider.cs:104)
    Rethrow as JsonSerializationException: Error getting value from 'linear' on 'UnityEngine.Color'.
     
  43. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    color has some properties that also return a color I believe. This causes stack overflow (infinite serialization). You'll have to write a custom jsonconverter for it and tell it not to serialize those properties.
     
  44. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Version 1.3.6 has been submitted to the Asset Store for review. For those who purchased through Fast Spring, the new 1.3.6 Version will be available in your parentElement account by end of day today and I'll send out an update notification. Here is the change log:

    === Version 1.3.6 Change Log ===
    [Feature] - Multidimensional Array Support
    [Feature] - BitArray Support
    [Bug Fix] - Time zone exception in Windows Phone 8 has been fixed
    [Bug Fix] - Fixed edge case where iOS still threw System.String does not implement IEnumerable
    [Bug Fix] - Fixed edge case where external assemblies on Android sometimes did not load
    [Bug Fix] - Fixed Windows Store Apps compile to account for Unity change in 4.3.3
    [Bug Fix] - Fixed JObject property removal on Web Player
    =================================
     
  45. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Version 1.3.6 has been updated on parentElement.com. Those of you who purchased through FastSpring can now download the 1.3.6 Release. For the rest, it will be available as soon as the Asset Store approves it.
     
  46. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Someone sent me an email and reminded me that I still needed to address the issue of using Enums as Keys for a Dictionary (i.e. Dictionary<MyEnum, String>). Enums work fine as keys but they are always converted to their string representation for Dictionaries and not their Int value. There is actually a good reason for this and it's not a bug, but necessary in order to produce valid JSON.

    Dictionaries in .NET are equivalent to associative arrays in JavaScript. JSON is about producing valid javascript notation. Arrays in JavaScript can really be handled a couple of ways. One way is the associative, the other is by index. So now let's say we have a Dictionary<string, string> and we add to it "Foo", "Bar". In JavaScript, this is an associative arrays so myArray["Foo"] == "Bar" will be true. The problem is that associative arrays have to have strings as keys. An integer cannot be a key. If you said: myArray[4] == "Bar" it's going to check based on the index of 4, not a value.

    So, because of this Enums cannot be serialized as integers and used as dictionary keys. Of course, you actually could read and write the JSON yourself by creating a custom ContractResolver and using it for your Dictionaries but it would take some work and your JSON won't be compatible with other platform libraries or standard web based javascript unless you customize that code as well.
     
  47. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Version 1.3.6 has been approved and is live on the asset store. There may be a small bug with multidimensional arrays as it seems a manual conflict resolution on a code merge may have missed a line (the manual part would be me :/ ). I need to take a look at that and will patch if possible and get another push to the asset store.

    One other note, as of Unity 4.5, the test scene included will generate errors related to exceeding maximum vertices. This is not JSON .NET throwing the error but rather what appears to be a Unity bug when using Debug.Log with large strings which I do in my test scene / behavior.
     
  48. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Based on some user feedback, I'm going to start working on some documentation specific to certain Unity items. I am also going to start working on creating some custom converters to provide as extras. They will eventually be available on the asset store inside the JSON .NET package but I wanted to get some additional feedback.

    I have attached a JsonConverter for Matrix4x4 that ignores the inverse and transpose properties to avoid infinite loop serialization. I'd love to hear some feedback on other types of converters you'd like to see.

    Here are the instructions for using the Matrix4x4Converter

    There are a couple of ways to use it. If you have a class with a Matrix4x4 property you can add it as an attribute so like this:
    Code (csharp):
    1.  
    2. public class Foo
    3. {
    4.   [JsonConverter(typeof(Matrix4x4Converter))]
    5.   public Matrix4x4 Bar { get; set; }
    6. }
    7.  
    Now, you may be working with external libraries or objects that contain a Matrix4x4, in which case you can use the SerializeObject overload that accepts Converter parameters like so:

    Code (csharp):
    1.  
    2. var json = JsonConvert.SerializeObject(myObject, Formatting.None, new Matrix4x4Converter());
    3.  
    There's no need to use it for DeserializeObject because those properties don't get deserialized anyway and in fact the Converter has CanRead set to false so the converter would be ignored anyway (in the event it's used as an attribute as in example 1).

    With Converters, you can pass multiple converters to the parameter array or you can modify converters to work on multiple types. Also, to use this you'll need to include the namespace that it's in. Put this file anywhere you'd like.. I'll eventually be adding more and including in a separate .unitypackage:

    Code (csharp):
    1.  
    2. using Assets.JsonDotNet.Extras.CustomConverters;
    3.  
     

    Attached Files:

  49. mcmorry

    mcmorry

    Joined:
    Dec 2, 2012
    Posts:
    580
    The idea is that you could convert the key to a string using the integer value, so you will have this json:
    Code (JavaScript):
    1. var obj = {'1': 'first', '2': 'second'}
    and this is a valid JS array. You will access with:
    Code (JavaScript):
    1. obj['1']
    Anyway this is the code that I wrote also with your suggestions:
    Code (CSharp):
    1. using Newtonsoft.Json;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6.     public class DictionaryEnumKeyConverter<TKey, TValue> : JsonConverter {
    7.  
    8.         public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
    9.             var intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(int), typeof(TValue));
    10.             var intermediateDictionary = (IDictionary)Activator.CreateInstance(intermediateDictionaryType);
    11.        
    12.             foreach (DictionaryEntry pair in (IDictionary)value)
    13.                 intermediateDictionary.Add(Convert.ToInt32(pair.Key), pair.Value);
    14.        
    15.             serializer.Serialize(writer, intermediateDictionary);
    16.         }
    17.    
    18.         public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
    19.             if (reader.TokenType == JsonToken.Null)
    20.                 return null;
    21.        
    22.             var intermediateDictionaryType = typeof(Dictionary<,>).MakeGenericType(typeof(int), typeof(TValue));
    23.             var intermediateDictionary = (IDictionary)Activator.CreateInstance(intermediateDictionaryType);
    24.             serializer.Populate(reader, intermediateDictionary);
    25.        
    26.             var finalDictionary = (IDictionary)Activator.CreateInstance(objectType);
    27.             foreach (DictionaryEntry pair in intermediateDictionary)
    28.                 finalDictionary.Add((TKey)pair.Key, pair.Value);
    29.        
    30.             return finalDictionary;
    31.         }
    32.    
    33.         public override bool CanConvert(Type objectType) {
    34.             return objectType.IsA(typeof(IDictionary<,>)) &&
    35.                 objectType.GetGenericArguments()[0].IsA(typeof(TKey));
    36.         }
    37.    
    38.     }
    39.  
    And this is the usage:
    Code (CSharp):
    1. // given a Dictionary<MyEnum, MyValue> inside the 'obj' instance
    2. string json = JsonConvert.SerializeObject(obj, new DictionaryEnumKeyConverter<MyEnum, MyValue>());
    I hope that it will also work for other developers.

    UPDATE:

    You will also need this extension method for the Type class:
    Code (CSharp):
    1.  
    2.     public static class TypeExtensions {
    3.         public static bool IsA(this Type type, Type typeToBe) {
    4.             if (!typeToBe.IsGenericTypeDefinition)
    5.                 return typeToBe.IsAssignableFrom(type);
    6.            
    7.             var toCheckTypes = new List<Type> { type };
    8.             if (typeToBe.IsInterface)
    9.                 toCheckTypes.AddRange(type.GetInterfaces());
    10.            
    11.             var basedOn = type;
    12.             while (basedOn.BaseType != null) {
    13.                 toCheckTypes.Add(basedOn.BaseType);
    14.                 basedOn = basedOn.BaseType;
    15.             }
    16.            
    17.             return toCheckTypes.Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeToBe);
    18.         }
    19.     }
     
    Last edited: Aug 4, 2014
    Dustin-Horne likes this.
  50. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Thanks mcmorry, I forgot to mention in my previous post that this was also on my list of custom converters to create and include with the package. :) The point of my comment about the invalid JSON was that this will need to be done with a converter and not automatically (as you did) and that if you send this to a server that's using JSON .NET you'll need to use the same converter when deserializing.