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\"}]"