10

I have a set of complex business objects that I'd like to serialize to Json for use in a web service. I'm currently using the DataContractJsonSerializer to product the Json, but it balks on deserialization because the default XmlReader can't handle Base64 strings.

After reading many positive reviews of Json.Net, I decided to give it a try. Surprisingly, the simplest case produces erroneous output if the business object overrides the ToString() method. Instead of generating JSON, it simply emits the string value.

For example, the following statement produces only a string, as the serializer appears to view the object as a simple string.

public class MyClass {
    public string Title{get;set;}
    public override ToString(){ return Title; }
    public string ToJson(){ 
        return JsonConvert.SerializeObject(this); 
    }
}

Instead of json formatted output, all I get is the title string. Do I have to mark the object in some special way to avoid this? Since the business object hierarchy includes many objects that override ToString(), I would rather avoid having to introduce special attributes, etc.

John Holliday
  • 1,268
  • 1
  • 11
  • 19

4 Answers4

4

Is it possible that your actual class has a TypeConverterAttribute attached to it? I just ran into the exact same problem and found out that the TypeConverterAttribute was causing this. In thast case, this might help (at least it did for me).

This is very bad because you can inadvertently break your program (by simply adding a TypeConverter maybe for displaying the object in a PropertyGrid) without getting a compiler warning...

using Newtonsoft.Json;
using System;
using System.ComponentModel;

namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var with = new WithTypeConverter() { Bla = 12, Blub = "With" };
            var without = new WithoutTypeConverter() { Bla = 12, Blub = "Without" };

            Console.WriteLine(with);
            Console.WriteLine(JsonConvert.SerializeObject(with));

            Console.WriteLine(without);
            Console.WriteLine(JsonConvert.SerializeObject(without));
            Console.ReadKey();
        }
    }

    public class baseClass
    {
        public int Bla { get; set; }
        public string Blub { get; set; }

        public override string ToString()
        {
            return String.Format("{0}: {1}", this.GetType().Name, Blub);
        }
    }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class WithTypeConverter : baseClass
    {
    }

    public class WithoutTypeConverter : baseClass
    {
    }
}
Community
  • 1
  • 1
wexman
  • 1,217
  • 10
  • 19
2

You may be testing this wrong. I just ran the following code in LINQPad:

void Main()
{
    new MyClass{Title = "hi"}.ToJson().Dump();
}

// Define other methods and classes here
public class MyClass {
    public string Title{get;set;}
    public override string ToString(){ return Title; }
    public string ToJson(){ 
        return JsonConvert.SerializeObject(this); 
    }
}

Output:

{"Title":"hi"}
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
2

I suspect you are using MyClass as the key in a dictionary or hashtable?

Linqpad example:

void Main()
{
    object thing = new Dictionary<MyClass, MyClass>() { 
        { 
            new MyClass { Title = "hi" }, new MyClass { Title = "bye" } 
        }
    };  
    JsonConvert.SerializeObject(thing).Dump();
}

public class MyClass
{
    public string Title { get; set; }
    public override string ToString() { return "BROKEN"; }
}

Output:

{"BROKEN":{"Title":"bye"}}

This is the expected behaviour as there is no way to express a complex object as a key in json.

To work around this either change your model or implement a TypeConverter. If your object is simple enough you could have the ConvertTo and ConvertFrom simply read and write parameters in a given order.

[EDIT]

This turned out to be more simple than I'd expected. Here is my JsonConverter solution.

public class ObjectKeyDictionaryTypeConverter<T1, T2> : JsonConverter<Dictionary<T1, T2>>
{   
    public override void WriteJson(JsonWriter writer, Dictionary<T1, T2> value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value.ToArray());
    }

    public override Dictionary<T1, T2> ReadJson(JsonReader reader, Type objectType, Dictionary<T1, T2> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var items = serializer.Deserialize(reader) as KeyValuePair<T1,T2>[];
        return items?.ToDictionary(a => a.Key, a => a.Value);
    }
}

Usage:

[JsonConverter(typeof(ObjectKeyDictionaryTypeConverter<ICriteria, Results>))]
public Dictionary<ICriteria, Results> SearchesAndResults { get; set; }
WillDud
  • 347
  • 1
  • 14
1

Use JsonSerializer from System.Text.Json to serialize the class. Like so:

using System.Text.Json;
...


    public class Foo{
    Public String Title {get;set;}
    
        public override ToString(){
        return JsonSerializer.Serialize<Foo>(this);
        }
    }

Docs: https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer?view=netcore-3.1

Context: https://youtu.be/JfnTG955cuk?t=406