1

For the project at hand I have to use DataContractJsonSerializer for serialization and have to generate a specific output based on the member's values.

My class looks similar to this:

public class MyClass
{
  public string foo;
  public string bar;

  public MyClass(string f, string b = "")
  {
    this.foo = f;
    this.bar = b;
  }
}

Now serialization of a list like

var list = new List<MyClass>
{
  new MyClass("foo", "bar"),
  new MyClass("foo1"),
  new MyClass("foo2", "bar2")
};

should look like this

[{"foo": "bar"}, "foo1", {"foo2": "bar2"}]

or - better yet - escaped and as a string:

"[{\"foo\": \"bar\"}, \"foo1\", {\"foo2\": \"bar2\"}]"
  • A mixture of strings and objects. How could I achieve this?

I tried to override the ToString() method and serializing the corresponding strings resulting in unnecessarily escaped symbols, e.g. bar2 could be m/s and was escaped as m\\/s which could not be deserialized correctly on the web server.

Finally, I just need to serialize to this format. There is no need to deserialize this format with DataContractJsonSerializer.

dbc
  • 104,963
  • 20
  • 228
  • 340
vonludi
  • 419
  • 2
  • 20
  • I believe there is no way to do this purely with `DataContractJsonSerializer` because it shares a code base with `DataContractSerializer` for serializing [data contract types](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/types-supported-by-the-data-contract-serializer) and thus doesn't provide access to the underlying serialized format (JSON or XML). – dbc Aug 17 '17 at 21:15
  • This would be easy to do if you switched to [tag:json.net]. Alternatively, you could call [`JsonReaderWriterFactory.CreateJsonWriter()`](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/types-supported-by-the-data-contract-serializer) and manually serialize your own JSON output following the rules given in [Mapping Between JSON and XML](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/mapping-between-json-and-xml). – dbc Aug 17 '17 at 21:17
  • I just need to serialize to this format. – vonludi Aug 18 '17 at 13:57

2 Answers2

1

What you would like to do is to conditionally replace instances of MyClass with a serialization surrogate that is a string or a dictionary, however using a primitive as a surrogate is not supported by data contract serialization, as explained here by Microsoft.

However, since you only need to serialize and not deserialize, you can get the output you need by manually replacing your List<MyClass> with a surrogate List<object> in which instances of MyClass are replaced with a string when bar is empty, and a Dictionary<string, string> otherwise. Then manually construct a DataContractJsonSerializer with the following values in DataContractJsonSerializerSettings:

(Note that DataContractJsonSerializerSettings, EmitTypeInformation and UseSimpleDictionaryFormat are all new to .NET 4.5.)

Thus you could define your MyType as follows:

public interface IHasSerializationSurrogate
{
    object ToSerializationSurrogate();
}

public class MyClass : IHasSerializationSurrogate
{
    public string foo;
    public string bar;

    // If you're not going to mark MyClass with data contract attributes, DataContractJsonSerializer
    // requires a default constructor.  It can be private.
    MyClass() : this("", "") { }

    public MyClass(string f, string b = "")
    {
        this.foo = f;
        this.bar = b;
    }

    public object ToSerializationSurrogate()
    {
        if (string.IsNullOrEmpty(bar))
            return foo;
        return new Dictionary<string, string> { { foo, bar } };
    }
}

Then introduce the following extension methods:

public static partial class DataContractJsonSerializerHelper
{
    public static string SerializeJsonSurrogateCollection<T>(this IEnumerable<T> collection) where T : IHasSerializationSurrogate
    {
        if (collection == null)
            throw new ArgumentNullException();
        var surrogate = collection.Select(i => i == null ? null : i.ToSerializationSurrogate()).ToList();
        var settings = new DataContractJsonSerializerSettings
        {
            EmitTypeInformation = EmitTypeInformation.Never,
            KnownTypes = surrogate.Where(s => s != null).Select(s => s.GetType()).Distinct().ToList(),
            UseSimpleDictionaryFormat = true,
        };
        return DataContractJsonSerializerHelper.SerializeJson(surrogate, settings);
    }

    public static string SerializeJson<T>(this T obj, DataContractJsonSerializerSettings settings)
    {
        var type = obj == null ? typeof(T) : obj.GetType();
        var serializer = new DataContractJsonSerializer(type, settings);
        return SerializeJson<T>(obj, serializer);
    }

    public static string SerializeJson<T>(this T obj, DataContractJsonSerializer serializer = null)
    {
        serializer = serializer ?? new DataContractJsonSerializer(obj == null ? typeof(T) : obj.GetType());
        using (var memory = new MemoryStream())
        {
            serializer.WriteObject(memory, obj);
            memory.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(memory))
            {
                return reader.ReadToEnd();
            }
        }
    }
}

And serialize your list to JSON manually as follows:

var json = list.SerializeJsonSurrogateCollection();

With the following result:

[{"foo":"bar"},"foo1",null,{"foo2":"bar2"}]

If you really need the string to be escaped (why?) you can always serialize the resulting string to JSON a second time with DataContractJsonSerializer producing a double-serialized result:

var jsonOfJson = json.SerializeJson();

Resulting in

"[{\"foo\":\"bar\"},\"foo1\",{\"foo2\":\"bar2\"}]"
dbc
  • 104,963
  • 20
  • 228
  • 340
  • I think this is the right direction. Still, the output I needed is given as `"[foo value]"` if bar is empty and `{"[foo value]" : "[bar value]"}` when it isn't. – vonludi Aug 19 '17 at 08:08
  • @vonludi - can't believe I missed that. Answer updated. – dbc Aug 19 '17 at 19:16
  • Thank you for your help. Regarding the "Why escape?" question: I think that I need to escape strings when using them in a httpwebrequest according to this answer https://stackoverflow.com/questions/20510437/use-c-sharp-httpwebrequest-to-send-json-to-web-service/20511017#20511017 – vonludi Aug 22 '17 at 09:12
  • Thank you for your help. This gave me the right idea. – vonludi Aug 31 '17 at 15:49
0

Deserialize will happen based on name and value combination so your text should be like this [{"foo": "foo","bar": "bar"},{"foo": "foo1"},{"foo": "foo2","bar": "bar2"}]

Suresh
  • 96
  • 4
  • So, are you saying it is not in any way possible to override or implement to get the required output? This would be {[value of foo]: [value of bar]} if there is a non-empty bar and [value of foo] otherwise. – vonludi Aug 17 '17 at 14:02