Search Unity

JSON .NET for Unity

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

  1. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    JSON .NET is a validating converter so it doesn't play as fast and loose with JSON rules. In this case if you wanted that to automatically be parsed into a string the brackets would also need to be in quotes and the other quotes escaped. But, it is possible to make it parse as a string.

    What you'll need to do is create a custom JsonConverter. You could call it JsonAsStringConverter. You'll only need to override the ReadJson method and then just output it as a string. Then, to use it you'd just modify your Response class to look like this:

    Code (csharp):
    1.  
    2. using Newtonsoft.Json;
    3.  
    4. public class ServerResponse
    5. {
    6.     public ResponseStatus status;
    7.  
    8.     [JsonAsStringConverter]
    9.     public string data;
    10. }
    11.  
    This would make that converter automatically get used for that property. If you want, take a look at the /Extras folder which has custom converters. Those override the WriteJson method as they are used to selectively serialize properties.

    Here is the documentation for the ReadJson method:
    http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonReader.htm

    To give you a head start, you'll probably need to create a JObject from the reader parameter then call ToString on the JObject which will give you the Json representation. Then just return that. Give it a shot and if you're still stuck I can help you write it when I get freed up.
     
    Enzign and tosiabunio like this.
  2. truffle_shuffle

    truffle_shuffle

    Joined:
    Dec 30, 2014
    Posts:
    16
    I keep getting this error and have no idea how to debug it:
    Code (csharp):
    1.  
    2. JsonSerializationException: Self referencing loop detected for type 'UnityEngine.GameObject'.
    3. Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.CheckForCircularReference (System.Object value, Nullable`1 referenceLoopHandling, Newtonsoft.Json.Serialization.JsonContract contract) (at Assets/JsonDotNet/Source/Serialization/JsonSerializerInternalWriter.cs:238)
    4. Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteMemberInfoProperty (Newtonsoft.Json.JsonWriter writer, System.Object memberValue, Newtonsoft.Json.Serialization.JsonProperty property, Newtonsoft.Json.Serialization.JsonContract contract) (at Assets/JsonDotNet/Source/Serialization/JsonSerializerInternalWriter.cs:215)
    5. 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:341)
    Background Info:
    I've got a ScriptableObject that I'm trying to use as a character configuration. On Awake() I'm trying to copy the contents of that config file into a new object for each character. That new object will then be the unique configuration for each character. Now I can track things like distanceToPlayer, jumpState ....etc. for that specific clone.

    Cloning Method:

    Code (csharp):
    1.  
    2.     //  Clone an object
    3.     public static class Clone
    4.     {          
    5.         public static T CloneJson<T>(this T source)
    6.         {
    7.             //  new JsonSerializerSettings()
    8.             //  {
    9.             //      //  ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
    10.             //     //  PreserveReferencesHandling = PreserveReferencesHandling.Objects
    11.             //      //  ReferenceLoopHandling = ReferenceLoopHandling.Ignore
    12.             //  };
    13.  
    14.  
    15.            // Don't serialize a null object, simply return the default for that object
    16.            if (Object.ReferenceEquals(source, null))
    17.            {
    18.             //  Debug.Log(source.GetType());
    19.                return default(T);
    20.            }
    21.      
    22.            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source));
    23.         }
    24.     }
    25.  
    Creating the Clone:
    Code (csharp):
    1.  
    2. ragdollConfig = Clone.CloneJson<CharacterConfiguration>(ragdollCharacterConfig.characterConfiguration);
    3.  
    The Object I'm trying to Clone: (kinda long so it's on pastebin)
    http://pastebin.com/2WHMY26G



    Can you help me troubleshoot this OR....If there is a more sensible way to accomplish this I am open to restructuring things. Ultimately, I want to configure and provision my different types of characters using scriptableobjects, and then apply those settings to the characters at runtime. You will notice I'm using BaseBehavior instead of MonoBehavior. That's because I'm using FullInspector.
     
    Last edited: Aug 4, 2015
  3. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Oh wow... that's a lot of stuff. We kind of have to narrow it down a bit (could you edit your post with code tags?). Is BaseScriptableObject a FullInspector object? Does it inherit ScriptableObject? I'd definitely have to take a look at this... it does have to do with reference loop handling but the depth might be an issue. I think the main issue is that you're serializing a GameObject which will have a Transform which has a Parent property pointing back to the main GameObject.

    I'd really need to get a complete repro from you so I can see exactly what's breaking. It can have FullInspector included as I've worked with @sient in the past to improve compatibility between my asset and FullInspector. Just make sure you email it to me, don't post a link publically.
     
  4. truffle_shuffle

    truffle_shuffle

    Joined:
    Dec 30, 2014
    Posts:
    16
    1. Sorry, had trouble finding the code tag
    2. Yes BaseScriptableObject inherits from ScriptableObject
    3. I had it working before but it seemed like it stopped working after I added a reference in my character config file. This:
    Code (CSharp):
    1. public RPC_AnimationClip_BSO standingClip;
    I'm using lots of scriptable objects and this line above is a BaseScriptableObject which defines my standing animation. On Awake() I iterate through that Animation Clip and assign rigidbodies based on the config. So, in short, that does reference rigidbodies. I will have to see if I can make a test project to send if need be.
     
  5. truffle_shuffle

    truffle_shuffle

    Joined:
    Dec 30, 2014
    Posts:
    16
    Let me revise my question.

    How could I properly serialize/deserialize a class that has a reference to a Rigidbody? Something like this:
    Code (CSharp):
    1.        
    2. public class JumpingBodyPart
    3.         {
    4.                 public bool enabled = true;
    5.                 public string bodyPartName;
    6.                 public Rigidbody bodyPartRigidbody;
    7.                 public float jumpForce;        
    8.         }
    Also, how would I serialize/deserialize a class that contained a reference to the JumpingBodyPart class?
    Code (CSharp):
    1. public class TestClass
    2. {
    3.      public JumpingBodyPart testJumpingBodyPart;
    4. }
     
  6. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    That makes more sense. So there are some Unity objects that are not supported out of the box like Monobehaviour and GameObject because they create circular references but also because some members only pretend to be null as Unity has overridden the == operator, for instance, rigidBody on a Monobehaviour which will appear not to be null using reflection but will throw a Unity exception when accessed if it hasn't been added.

    If you could send me a repro project that would be great because I'm going to have to step through and figure out exactly which member(s) cause the problem but I have a sneaking suspicion of what it is. Your bodyPartRigidbody is a Rigidbody type which inherits from Component and thus has a gameObject field which has a reference to a gameObject and thus it's causing a circular dependency.

    It certainly is possible to get this system to all work but it's going to require the creation of custom JsonConverters to handle the data.
     
  7. truffle_shuffle

    truffle_shuffle

    Joined:
    Dec 30, 2014
    Posts:
    16
    Dustin, I've got it working now. I'm decorating all of my Rigidbody and Transform references in my ScriptableObjects with [System.NonSerialized] so the references wouldn't be serialized. Since I'm looping through body parts and assigning Rigidbodies on Awake(), this works fine for me now. The problem arose when my ScriptableObject asset files (CharacterConfig for example) was storing a {fileID: 0} for every Rigidbody or Transform in the _objectReferences: field. The next character that tried to use that SAME CharacterConfig file would throw the self referencing loop error.

    The take away here is: IF YOU REFERENCE GAMEOBJECTS IN A SCRIPTABLEOBJECT AND THEN WANT TO SERIALIZE IT, YOU NEED TO MAKE SURE YOU MARK THOSE OBJECTS WITH [System.NonSerialized].

    Man, I hope that made sense.
     
    Dustin-Horne likes this.
  8. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Awesome news, and yeah if you're repopulating no need to serialize at all. You can also use the [JsonIgnore] attribute if you're specific to Json .NET.
     
  9. mattclarke

    mattclarke

    Joined:
    Aug 5, 2015
    Posts:
    1
    Is there a way to globally set all enums to be serialized as strings, rather than ints? I know I can use attributes, or pass a JsonConverter when serializing but I'd rather set the default if possible.

    I found an old blog post which suggests that enums are serialized to strings by default but that no longer seems to be the case. It also explains that JsonSerializerSettings.DefaultEnumSerializationHandling can be used to change this, which doesn't seem to exist any more.
     
  10. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I should edit or delete that post. During the porting of JSON .NET I had added that but removed it because it was inconsistent with how the official release handled Enum serialization. I do plan to expose the functionality again in the future when I get the port upgraded to a newer version. For now it is still possible but requires a code change.

    Open up:
    /JsonDotNet/Source/Serialization/DefaultContractResolver.cs

    The code isn't super clean in this file as the formatting got weird, but starting on line 85 you'll see where the BuiltInConverters are added. These are the ones that are used by default. Line 85 should be:
    Code (csharp):
    1.  
    2. new KeyValuePairConverter(),
    3.  
    Below it is a conditional for XmlNodeConverter. Just change line 85 to the following two lines:

    Code (csharp):
    1.  
    2. new KeyValuePairConverter(),
    3. new StringEnumConverter(),
    4.  
    That should add StringEnumConverter to the list of default converters and it should then be used automatically.
     
  11. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    @Dustin Horne I'm hosting a json file on Parse but after download it has a BOM at the start and this trips up JsonTextReader. Is there a setting to allow it? Should there be?
     
  12. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Unfortunately there's not an automatic way to do it. I'm not sure how Parse processes and returns the data but if it can return it as a stream, you can use a streamreader and skip the first byte (or two bytes if it uses the zero width space) when reading the JSON string. Otherwise this may help:


    http://stackoverflow.com/questions/1317700/strip-byte-order-mark-from-string-in-c-sharp
     
  13. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    I've found that string.Trim cleans it up for json reading.
     
    Dustin-Horne likes this.
  14. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Interesting, good to know!
     
  15. RC_ID3I

    RC_ID3I

    Joined:
    Dec 10, 2014
    Posts:
    3
    Hi Dustin !

    We use your pluggin in our projects and i'ts a very good asset !

    That said, we enconter an AOT problem on iOS when using it inside a Mono DLL.
    It works smoothly when we include the code directely inside the unity project but when creating an external DLL that we include inside the plugin folder it crashes ios with AOT thing.

    We are forced to do this to avoid circular references (we work on big project with lot of cross-platform code)

    Do you have an idea to make this work ?

    Thanks !
     
  16. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Thanks! If you want to pre-compile it you'll have to add the symbol for ios in the project settings. Put the source into Visual Studio, then go to project properties, build and for symbols add:
    UNITY_IOS

    It compiles different bits of code and Unity specific workarounds depending on the platform you provide it which is why it has to be compiled with that symbol.

    Then compile to your DLL. The resulting DLL will actually work in editor, for standalone, mac, and WebGL, Xbox One, PS4, Xbox 360 and Wii but it will not work for Android, Windows Store / Universal / Phone or Web Player. If you need it precompiled for those you'll have to do separate builds one at a time with the following symbols:

    UNITY_ANDROID
    UNITY_WINRT
    UNITY_WEBPLAYER

    The other thing you can do is precompile for each platform and put those assemblies in your project if you're using Unity 5, and then there is a setting for each DLL in Unity 5 where you can tell it which platform(s) it's for. I haven't tried it yet, but it should work.

    What platforms are you targeting and what's causing your circular dependency?
     
  17. RC_ID3I

    RC_ID3I

    Joined:
    Dec 10, 2014
    Posts:
    3
    Waw what a lighting fast reply !

    Thanks a lot Dustin this worked like a charm !

    The original program was working on Windows (7/8) platforms, then we ported it to iOS and Androïd.
    We will need to build the DLL only once for Androïd and iOS, so right now we will do the builds manually each time for each platform.

    Thanks again !
     
    Dustin-Horne likes this.
  18. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You're very welcome. :)
     
    RC_ID3I likes this.
  19. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    Hello. Is there any documentation of how to use\ write serialization for custom types?
    I use Anti-Cheat Toolkit https://www.assetstore.unity3d.com/en/#!/content/10395
    This plugin have many "obscured" types.
    If I serialize
    public ObscuredInt LevelNumber;
    I have in json
    "LevelNumber":{},

    ObscuredInt may dynamic convert to type int;
    ObscuredInt LevelNumber = 5;
    int LevelNumberB = LevelNumber;
     
  20. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Everything relies on the official documentation, though there are a few newer features that aren't currently ported. That being said, I'm not familiar with anti-cheat toolkit. What is your JSON supposed to look like? I'm wording if that asset uses [NonSerializable] attributes on it's object properties, in which case a custom json converter would have to be written to handle each of its types.


    http://www.newtonsoft.com/json/help/html/Introduction.htm
     
  21. echeg

    echeg

    Joined:
    Aug 1, 2012
    Posts:
    90
    I want serialize to int. Like
    {"LevelNumber":99}
    I write
    public class ObscuredIntConverter : JsonConverter
    {
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    int v = (ObscuredInt) value;
    IConvertible convertible = v as IConvertible;
    writer.WriteValue( convertible.ToUInt32(CultureInfo.InvariantCulture));
    }

    public override bool CanConvert(Type objectType)
    {
    return objectType == typeof(ObscuredInt);
    }
    }
    And serilize ok. But now I can not figure out how to deserialize the json
    Could you tell me what you want to replace:
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
    throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }
     
  22. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Ok, I'll take a look at it later this evening. I can only guess as to how to create the converter so you'll have to modify what I write to fix it as I don't have access to the AntiCheat toolkit and have no idea how the data is actually structured or how those classes are composed.
     
  23. Unity1Wagawin

    Unity1Wagawin

    Joined:
    Jul 9, 2015
    Posts:
    2
    Hello Dustin,

    we are using your version to serialize and deserialize objects to and from json. While coding i hit a problem where i think you could help me:

    Simple class Treasure with a string property
    Code (CSharp):
    1. namespace Assets.AppEntities {
    2.     class Treasure {
    3.         public string value;
    4.     }
    5. }
    Json serialization with
    Code (CSharp):
    1. Treasure treasure = new Treasure();
    2.                         treasure.value = "TestString";
    3.                         string json = JsonConvert.SerializeObject(treasure,Formatting.None,JsonUtils.GetJsonSerializerSettings());
    with settings
    Code (CSharp):
    1. public static JsonSerializerSettings GetJsonSerializerSettings() {
    2.             JsonConverter[] converter = new JsonConverter[2];
    3.             IsoDateTimeConverter isoDateTimeConverter = new IsoDateTimeConverter();
    4.             isoDateTimeConverter.DateTimeFormat = dateTimeFormat;
    5.             converter[0] = isoDateTimeConverter;
    6.             StringEnumConverter stringEnumConverter = new StringEnumConverter();
    7.             converter[1] = stringEnumConverter;
    8.             JsonSerializerSettings settings = new JsonSerializerSettings {
    9.                 TypeNameHandling = TypeNameHandling.All,
    10.                 NullValueHandling = NullValueHandling.Ignore,
    11.                 Converters = converter
    12.             };
    13.             return settings;
    14.         }
    leads to the following output in the json string
    {"$type":"Assets.AppEntities.Treasure, Assembly-CSharp","value":"TestString"}

    As you can see there is this ", Assembly-CSharp" stuff. Since we are using json to communicate with a java backend it don't want that in the json :(

    Am i doing something wrong? Any help would be really great!

    Best regards,
    Chris
     
  24. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Remove TypeNameHandling.All from your settings. That setting is used for polymorphism and you don't need it in this case.
     
  25. Unity1Wagawin

    Unity1Wagawin

    Joined:
    Jul 9, 2015
    Posts:
    2
    Hello Dustin,

    changing the Settings from All to Auto solved the Problem (i need this for abstract classes). Thank you very very much for your help!!

    Best regards,
    Chris
     
    Dustin-Horne likes this.
  26. elhispano

    elhispano

    Joined:
    Jan 23, 2012
    Posts:
    52
    Hi! I have readed some people talking about problems when serializing a Color property due to StackOverflowException when triying to serialize get_linear property (because it return a Color).

    I am trying to write a custom Converter without any success.

    Would you mind putting some example? I thinks is a very common scenario to store a color (for customization for example).

    Thanks you in advance.
     
  27. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Sure. Could you go ahead and post what you have so far? I'll help you get it fixed up to work.
     
  28. DirtyHippy

    DirtyHippy

    Joined:
    Jul 17, 2012
    Posts:
    224
    So I am converting a horde of xmlserialized stuff over to json and so far it has been pretty easy. However, it doesn't appear that type information is included when serializing enums from an object (in a property soup) regardless of typename handling. When using the string converter (which I prefer), it serializes the enum's string just fine, but when it deserializes it, the object's type is string - clearly it doesn't understand that the type is an enumeration (xml serializer includes the type name of the enum in this case and properly deserializes it). Serializing by value does appear to work, although I suspect it is not properly deserializing it into an enum in this case either but is rather allowing me to cast from an int. I've tried explicitly noting this object's property is an All for typenamehandling as well (no dice)

    Any suggestions here (other than using serialization by value)?

    I should note that first class types (i.e. classes, etc) stored as values in the property soup, seem to properly include their type information, and they serialize successfully.
     
    Last edited: Sep 1, 2015
  29. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    @DirtyHippy - Yeah, type name handle won't work in that case because enums are primitive types, just like sting, book and int. Enums actually I'm general use an in as their true value. Now, if the class you're deserializing to has an enum type for the property type then the deserializer will automatically convert the string or int value to the enum, but short of that it has no way to disseminate the type from the raw json since it looks the same as a string.

    I'd have to see how you're deserializing your data but it may be possible to write a custom converter to handle it. You wouldn't be able to just treat it as "object" though.
     
  30. DirtyHippy

    DirtyHippy

    Joined:
    Jul 17, 2012
    Posts:
    224
    Well I was able to hack it by serializing out a separate type for enums and post-processing the property soup entries after load and fixing the values.

    I also had to add support for ignoring TypeConverters in the core code, which was just a few lines, because I define a number of TypeConverters in my external editor that were being picked up in the internal query against TypeDescriptor. I'm not sure why this isn't part of core Json.net tbh, as it was clear others had the same issue via google.

    Everything is loading well, and my serialization time is cut in half, and I don't need to generate the silly run-time xml serializer dlls anymore (I was unable to use sgen). Thanks.
     
  31. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Glad it's working! Out of curiosity though, why would you want to ignore specific type converters? It kind of sounds like you're attempting to deserialize everything very generally, such as to play "objects" in some cases and in a way kind of working against the serializer. It works much better if you have properly typed concrete class implementations to deserialize to and in some cases where you may not it makes more sense to just use JObject and treat the data as key/value pair (or collections of JObjects).
     
  32. DirtyHippy

    DirtyHippy

    Joined:
    Jul 17, 2012
    Posts:
    224
    I add the TypeConverters dynamically to various types in my external Winforms editor. I have a lot of source, and lot of concepts that can be edited, so I based my editing off PropertyGrid with custom UITypeEditors for editing and TypeConverters since designing per-concept editing forms would be a huge amount of work. The TypeConverters are queried by the PropertyGrid at run time for all public properties (i.e. if you are not familiar, the property grid is just a reflection based editor). So for example, if I have a class that is public, the property grid will show a key/value entry for that class. If I don't supply a type converter, it just does a ToString() on the class (not useful). With a custom string TypeConverter, for a class that is reference to an Item, for example, I can show the actual name of the item (using custom logic in the editor only). These TypeConverters just convert the class to a readable name - clearly not something I want to use as a basis for serialization. Also, for a vast majority of my serialization, I do use strongly typed classes, but in one particular instance, I was using a property soup pattern.

    This use case was the one I saw others have the same problem with in JSon.Net.
     
  33. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    That makes perfect sense. I guess I never thought about how it would be applicable in the context of Unity. We actually have implemented a similar system at my daily grind but it is in the context of Windows Workflow. We're allowing workflows to be created as standalone DLL's and uploaded to the server in which we use the workflow editor to map inputs and outputs.
     
  34. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    655
    Hi, Dustin
    May I ask why ITraceWriter (and MemoryTraceWriter) is missing from non-WinRT version of JSON.Net for Unity, please?

    I won't claim to be an expert so figure the problem I'm having with default values would be best diagnosed by enabling tracing then watching the values but it seems JsonSerializerSettings.cs is missing the definitions whereas RT_JsonSerializerSettings.cs does have them?!

    Thanks!
     
  35. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    They will eventually be available for all platforms when I get the asset all upgraded to a newer version. For now though, the original port is based on an older release. When Unity added support for Windows Phone / Windows Store, it wasn't at all supported by the version of JSON .NET I had used so I retrofitted a separate version that is only used once you've built out for those platforms. That's why it's in the RT_ branch but not available in the editor or with other platforms.

    What issue are you having with default values?
     
  36. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    655
    Thanks, Dustin. OK, I'll post my stupidity in case you fancy being kind. (yes, I realise it's probably well known and I'm missing something.)

    I have a class GameSetup with a member that is an inner class (e.g. SubSetup). I'm using Unity serialization for GameSetup but allow players to load their own version from JSON. I want to let them omit any bits they don't want to specify. Problem comes with the default value for subSetup when it's not present in JSON.

    I believe I shouldn't specify it at its declaration point since that might annoy Unity serialization (right? hmm... now that I write this...?). I had thought I'd be able to instruct JSON deserializer via some attribute (JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)) to create a default value for subSetup (rather than null) but can't figure out how to say "instantiate default using SubSetup's default constructor".

    I suspect I probably need to specify a different constructor to GameSetup which sets good default values. I then hope JSON will then overwrite whichever bits as indicated in JSON.

    However before I started implementing that, I found the Populate() methods and am now thinking of using these (perhaps clone initial version from Unity serialization and use that to Populate() atop when loading JSON?).

    Here's a simplified chunk'o'code for clarity. Btw I'm using different serializers for different things (JSON for user configurability, default Unity for Unity Editor & ProtoBuf for wire serializing efficiency and cloning ... that and it's nice to be able to select things in or out for different cases).

    Code (CSharp):
    1.  
    2. [Serializable]
    3. [ProtoContract(UseProtoMembersOnly = true)]
    4. [JsonObject(MemberSerialization.OptIn)]
    5. class GameSetup {
    6.  
    7.     // (Other simple fields and an array that works fine)
    8.  
    9.     [SerializeField]
    10.     [ProtoMember(10)]
    11.     [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    12.     public SubSetup subSetup;
    13.  
    14.     [Serializable]
    15.     [ProtoContract(UseProtoMembersOnly = true)]
    16.     [JsonObject(MemberSerialization.OptIn)]
    17.     class SubSetup {
    18.         [ProtoMember(1)]
    19.         [JsonProperty]
    20.         public bool whatever;
    21.     }
    22.  
    23.     public static GameSetup loadFile(string filename) {
    24.         LogR.log(LogR.Level.ALL, "{0} loading from \"{1}\"", typeof(GameSetup).Name, filename);
    25.         using (var textReader = File.OpenText(filename)) {
    26.             JsonSerializerSettings settings = new JsonSerializerSettings();
    27.             using (var jsonReader = new JsonTextReader(textReader)) {
    28.                 var serializer = JsonSerializer.Create(settings);
    29.                 var setup = serializer.Deserialize<GameSetup>(jsonReader);
    30.                 LogR.log(LogR.Level.ALL, "{0} successfully loaded: {1}", typeof (GameSetup).Name, setup);
    31.                 return setup;
    32.             }
    33.         }
    34.     }
    35.  
    36. }
    37.  
     
  37. BJOwlient

    BJOwlient

    Joined:
    Jun 18, 2015
    Posts:
    4
    Hi,
    I'm beginning with this package and I wonder if there is a simple way to populate a list of scriptable objects from a json array ? The PopulateObject method doesn't seem to work in this case.
    Thank you.
     
  38. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    In this case I would create a custom JsonConverter for the class type SubSetup. In the Deserialize method, just create a new SubSetup and return it. That way even if the Json value is null it still gets created.
     
  39. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    What exactly isn't working? Are you getting an error or just not getting the data you need?
     
  40. mog-mog-mog

    mog-mog-mog

    Joined:
    Feb 12, 2014
    Posts:
    266
    I am trying to serialize/deserialize hashtable which gives me following AOT error on IOS
    Unity 5.1.2
    .net subset 2
    Strip byte code

    What am I missing here?


    'Newtonsoft.Json.Utilities.CollectionWrapper`1[[GoalManager+OneTimeGoals, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::.ctor' for which no ahead of time (AOT) code was generated.


    at System.Reflection.MonoCMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0

    at System.Reflection.MonoCMethod.Invoke (BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in <filename unknown>:0

    at System.Reflection.ConstructorInfo.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0

    at Newtonsoft.Json.Serialization.JsonArrayContract.CreateWrapper (System.Object list) [0x00000] in <filename unknown>:0

    at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue (Newtonsoft.Json.JsonWriter writer, System.Object value, Newtonsoft.Json.Serialization.JsonContract valueContract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContract collectionValueContract) [0x00000] in <filename unknown>:0

    at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value) [0x00000] in <filename unknown>:0

    at Newtonsoft.Json.JsonConvert.SerializeObject (System.Object value, Formatting formatting, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <filename unknown>:0

    at Newtonsoft.Json.JsonConvert.SerializeObject (System.Object value) [0x00000] in <filename unknown>:0
     
  41. BJOwlient

    BJOwlient

    Joined:
    Jun 18, 2015
    Posts:
    4
    When calling populate() on a previously created and filled list (with scriptable objects), the package still tries to instantiate the items of the list instead of populating those already there, so I get an error because of trying to call new ScriptableObject().
    But nevermind, I managed by creating my own JsonConverter.
     
  42. Arkade

    Arkade

    Joined:
    Oct 11, 2012
    Posts:
    655
    Thanks, Dustin. A two-part reply, one to check my understanding of your suggestion and a second to say what I did after writing my post and how it went.

    So that would be somewhat as suggested in this post? (or from this base)? Override ReadJson(), do serializer.Deserialize(reader, objectType), check null and return new instance?

    What I've already done now (so I'll stick with it) is as follows...

    So I did this and, after fixing (my version of the sample) code where I previously had ...

    Code (CSharp):
    1.     [SerializeField]
    2.     [ProtoMember(10)]
    3.     [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
    4.     public SubSetup subSetup;
    ... I needed to remove the DefaultValueHandling which caused absence to mean "overwrite with null". Once done, it works perfectly -- I start with a known good version from Unity Serialization from values set in the Editor, the player indicates to load, I clone my known good, Populate() atop it, any values they haven't specified leak through from the known good!

    Of course I'm always keen to hear if you think there are any issues that might come back to bite me with this! (Always a risk with something this subtle, huh!)

    Thanks again. Still appreciating your Asset :)
     
  43. mdgianino

    mdgianino

    Joined:
    Jun 28, 2015
    Posts:
    3
    Dustin,

    I have a class and enum built and compiled into a separate .dll that looks like the following:

    Code (CSharp):
    1.  
    2. public enum CGRequestType
    3. {
    4. DEBUG,
    5. LOGIN,
    6. LOGOUT,
    7. MATCH_INFO_QUERY
    8. }
    9. public class CGRequest
    10. {
    11. public CGRequestType RequestType { get; set; }
    12. public string RequestDataJson { get; set; }
    13.  
    14. public CGRequest(CGRequestType requestType, string requestDataJson)
    15. {
    16. RequestType = requestType;
    17. RequestDataJson = requestDataJson;
    18. }
    19. }
    20.  
    I then have some code that is suppose to serialize and deserialize these requests on the device. An example of where I am doing this is as follows:

    Code (CSharp):
    1.  
    2. int matchId = 2232;
    3. var request = new CGRequest(CGRequestType.MATCH_INFO_QUERY, JsonConvert.SerializeObject(matchId));
    4. Debug.Log(string.Format("REQUEST: {0}", request.ToString()));
    5. Debug.Log(string.Format("REQUEST SERIALIZED: {0}", JsonConvert.SerializeObject(request)));
    6.  
    When I run this code through the editor in Unity, everything works great. The output of the above block of code is:

    Code (CSharp):
    1. REQUEST SERIALIZED: {"RequestType":3,"RequestDataJson":"2232"}
    But if I build and run the same code on iOS, the output is blank and only says the following:

    Code (CSharp):
    1. REQUEST SERIALIZED: {}
    The class, as you can see, is very simple. But I built it into a separate .dll for portability. Like I said, it works great in the editor when I run it through Unity, but when I try it on iOS it fails to serialize and deserialize properly.

    What would cause this?
     
    Last edited: Sep 9, 2015
  44. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    My apologies for the delayed responses. I've been out of the office at a developer conference. I'll try to cover everything here:

    Something is getting stripped that you need. I'll be able to take a deeper look next week. I'd have to dive into the code to see what is being created via reflection, but I'm wondering if it isn't the parameterless constructor on HashTable that's being stripped.

    This is expected behavior if I understand you correctly. It will populate an existing list, but "Populate" doesn't translate all the way down to items within a collection, it just populates the collection itself. It has no way to know that the items in your JSON match specifically to the items in your collection. If you want to do it that way you'll need to instantiated an empty list and use Populate. Alternately, you could construct a JObject from your collection and do a one by one populate of the items in your list. I don't have the exact code in front of me but it would be something like:

    Code (csharp):
    1.  
    2. var myData = JObject.Parse(jsonString);
    3.  
    4. for(var i = 0; i < myList.Count; i++)
    5. {
    6.     JsonConvert.PopulateObject(myData[i].ToString(), myList[i]);
    7. }
    8.  
    Exactly! :). Looks good. And yeah that's exactly what I meant, check for null and create a new one.

    This sounds like an IL2CPP bug. Could you send me a repro so I can check it out? That way I can see if I can find a workaround.
     
  45. mog-mog-mog

    mog-mog-mog

    Joined:
    Feb 12, 2014
    Posts:
    266
    Thank you, no hurry. I tried changing Hashtable to list and serialization worked well. I am okay for now.

     
  46. mdgianino

    mdgianino

    Joined:
    Jun 28, 2015
    Posts:
    3
    What all do you need from me? Just a copy of the classes I used?
     
  47. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    If you could just put together a simple repro project and send it to me that would be great. The code that reproduces the problem with the Enum in a separate DLL. Email it to: dustin (at) parentelement (dot) com
     
  48. mdgianino

    mdgianino

    Joined:
    Jun 28, 2015
    Posts:
    3
    Shot you an email with the class library project as well as the unity project attached in a zip.

    I tested it in iOS and the CGRequest is still serializing to a blank value.
     
  49. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Thanks. Taking a look at it today.
     
  50. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Just a heads up to everyone, I've discovered that with the release of Unity 5.2, support for Windows Universal is completely broken. It seems that either Unity or Microsoft have put type extensions into System.Reflection that match the TypeExtensions within JSON .NET so there are ambiguous references. Even with those fixed, a final compile won't succeed and I'm not entirely sure yet whether IL.Emit works with the new Universal builds as Release uses .NET Native so I have a lot of troubleshooting and updating to do this week.

    There is currently no ETA on a working update for Windows Universal but I hope to have it by sometime next week. I have access to Windows 10 and the Windows Phone Insider Preview (fast ring / latest build) to test on as well.
     
    tosiabunio likes this.