0

I am having trouble with my serializing and deserializing. The below code has the List object record as a list while serializing however when deserializing it doesn't work.

Each Tag Parameter is of type object

TreeViewCollection = new ObservableCollection<TreeViewItem>()
        {
            new TreeViewItem()
            {
                Header = "Suite 1"
            },
            new TreeViewItem()
            {
                Header = "Suite 2",
                Nodes = new ObservableCollection<TreeViewItem>()
                {
                    new TreeViewItem()
                    {
                        Header = "Case 1"
                    }
                }
            },
            new TreeViewItem()
            {
                Header = "Suite 2",
                Nodes = new ObservableCollection<TreeViewItem>()
                {
                    new TreeViewItem()
                    {
                        Header = "Case 2",
                        Tag = new List<ListViewItem>()
                        {
                            new ListViewItem()
                            {
                                Tag = new ActionObject()
                                {
                                    Command = "Command",
                                    Target = "Target",
                                    Value = "Value",
                                    Comment = "Comment"
                                }
                            }
                        }
                    },
                    new TreeViewItem()
                    {
                        Header = "Case 3"
                    }
                }
            }
        };

        string serializedJson = JsonConvert.SerializeObject(TreeViewCollection, Formatting.Indented, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
        });

        var deserializedObject = JsonConvert.DeserializeObject<ObservableCollection<TreeViewItem>>(serializedJson, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Objects,
            TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
        });

        TreeViewCollection = deserializedObject;

However it comes out as JSON and not a List() object

{[
  {
    "$type": "RFA.Models.Items.ListViewItem, RFA",
    "ID": "0b9661f0-87f1-41f4-85cb-620dc1f49bb3",
    "Name": null,
    "Tag": {
      "$type": "RFA.Models.Objects.ActionObject, RFA",
      "Command": "Command",
      "Target": "Target",
      "Value": "Value",
      "Comment": "Comment"
    }
  }
]}

How can I get json deserialization to work with object types

Requested TreeviewItem class :

public partial class TreeViewItem
{
    #region Private Variables

    private string _header;
    private object _tag;
    private ObservableCollection<TreeViewItem> _nodes;

    #endregion
    #region Properties

    public string Header
    {
        get { return _header; }
        set
        {
            if (value == _header) return;
            _header = value;
            OnPropertyChanged();
        }
    }
    public object Tag
    {
        get { return _tag; }
        set
        {
            if (value == _tag) return;
            _tag = value;
            OnPropertyChanged();
        }
    }
    public ObservableCollection<TreeViewItem> Nodes
    {
        get { return _nodes; }
        set
        {
            if (Equals(value, _nodes)) return;
            _nodes = value;
            OnPropertyChanged();
        }
    }
    #endregion
}
A.Mills
  • 357
  • 3
  • 16
  • 1) Can you share the `TreeViewItem` type, or a simplified version thereof? Without a [mcve] it's hard to know your problem. Is it [`System.Windows.Controls.TreeViewItem`](https://msdn.microsoft.com/en-us/library/system.windows.controls.treeviewitem(v=vs.110).aspx)? 2) Can you clarify what you men by *However it comes out as JSON and not a List() object*? – dbc Mar 29 '17 at 17:09
  • I supplied the Treeviewitem class, this class is a 'copy' of the system.windows.controls.TreeViewItem. and no I cannot use the original class. When I deserialize the JSON string, instead of it parsing into a List object. Instead if stays as a json string. No errors are thrown but it messes with my code because my code is expecting a List. – A.Mills Mar 29 '17 at 17:21

1 Answers1

2

Your problem is that you are assigning a new List<ListViewItem>() value to an object Tag property, then serializing and deserializing with TypeNameHandling = TypeNameHandling.Objects. As explained in the Newtonsoft docs this enumeration has the following meanings:

None      0   Do not include the .NET type name when serializing types.
Objects   1   Include the .NET type name when serializing into a JSON object structure.
Arrays    2   Include the .NET type name when serializing into a JSON array structure.
All       3   Always include the .NET type name when serializing.
Auto      4   Include the .NET type name when the type of the object being serialized is not the same as its declared type. Note that this doesn't include the root serialized object by default. To include the root object's type name in JSON you must specify a root type object with SerializeObject(Object, Type, JsonSerializerSettings) or Serialize(JsonWriter, Object, Type).

By using TypeNameHandling.Objects you omit the type information for your List<ListViewItem> collection, and thus, when serialized and deserialized, your Tag gets turned into a LINQ-to-JSON JArray.

Instead, I recommend using TypeNameHandling.Auto. This includes type information only when necessary - i.e., when the declared type of a property value or collection item does not match the actual, observed type. Using this will cause type information to be emitted for your object Tag property but not for, say, your ObservableCollection<TreeViewItem> Nodes property which is fully typed.

Alternatively, if you can add Newtonsoft attributes to your model, you can mark the Tag property with [JsonProperty(TypeNameHandling = TypeNameHandling.All)]:

[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public object Tag { get; set; }

If you unconditionally require type information for your objects even when not needed for deserialization and cannot add Newtonsoft attributes to your model, you could use TypeNameHandling.All - but I don't really recommend this setting because it unconditionally emits type information for collections even when not necessary. This can cause problems down the road if, for instance, you decide to change the type of a collection from List<T> to ObservableCollection<T>. If you do, you'll need to strip out the collection type information during deserialization with something like IgnoreCollectionTypeConverter as shown in this answer. (Unfortunately, TypeNameHandling.Auto | TypeNameHandling.Objects seems not to be implemented.)

Finally, when using TypeNameHandling, do take note of this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json.

dbc
  • 104,963
  • 20
  • 228
  • 340