mhand
mhand

Reputation: 1277

Serialize complex type as simple type JSON

I have a complex type that is to be serialized as a string. I have this working as desired using IXmlSerializable and am now trying to get it to work for JSON messages as well. Currently I get it to serialize via the DataContractSerializer to the following:

<MyXmlRootElement>_value's contents</MyXmlRootElement>

For the JSON output I would like to just receive a JSON string with the _value's contents.

This type implements more than described below to also affect the generated wsdl through a few other attributes that hook into our web services framework. So the generated wsdl/xsd all looks great, it just isn't giving me the desired JSON.

[XmlRoot(ElementName = "MyXmlRootElement", Namespace = "MyNamespace"]
public class ComplexType : IXmlSerializable
{
    private string _value;
    public ComplexType(string value) { _value = value; }

    #region IXmlSerialiable Implementation

    public XmlSchema GetSchema() { return null; }

    public void ReadXml(XmlReader reader)
    {
        _value = reader.ReadString();
    }

    public void WriteXml(XmlWriter writer)
    {
        writer.WriteString(_value);
    }

    #endregion
}

I tried implementing ISerializable and this does affect the generated JSON, but it still places it in a complex (object) type, i.e, { "ValueKey": "_value's contents" }. Any idea how I can get it to serialize to a pure string, no curly braces?

Upvotes: 1

Views: 2126

Answers (2)

mhand
mhand

Reputation: 1277

The solution was easier than expected. You can register a JsonConverter with GlobalConfiguration. I made an abstract wrapper around JsonConverter as below, the idea for which came from the following SO thread: How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?.

public abstract class CustomJsonConverter<T, TResult> : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load token from stream
        var token = JToken.Load(reader);

        // Create target object based on token
        var target = Create(objectType, token);

        var targetType = target.GetType();
        if (targetType.IsClass && targetType != typeof(string))
        {
            // Populate the object properties
            var tokenReader = token.CreateReader();
            CopySerializerSettings(serializer, tokenReader);
            serializer.Populate(token.CreateReader(), target);
        }

        return target;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        T typedValued = (T)value;
        TResult valueToSerialize = Convert(typedValued);

        serializer.Serialize(writer, valueToSerialize);
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof (T) == objectType;
    }

    protected virtual T Create(Type type, JObject jObject)
    {
        // reads the token as an object type
        if (typeof(TResult).IsClass && typeof(T) != typeof(string))
        {
            return Convert(token.ToObject<TResult>());
        }

        var simpleValue = jObject.Value<TResult>();
        return Convert(simpleValue);
    }

    protected abstract TResult Convert(T value);
    protected abstract T Convert(TResult value);

    private static void CopySerializerSettings(JsonSerializer serializer, JsonReader reader)
    {
        reader.Culture = serializer.Culture;
        reader.DateFormatString = serializer.DateFormatString;
        reader.DateTimeZoneHandling = serializer.DateTimeZoneHandling;
        reader.DateParseHandling = serializer.DateParseHandling;
        reader.FloatParseHandling = serializer.FloatParseHandling;
    }
}

You can then use this to do something like the following

public class DateTimeToStringJsonConverter : CustomJsonConverter<DateTime, string>
{
    protected override string Convert(DateTime value)
    {
        return value.ToString();
    }

    protected override DateTime Convert(string value)
    {
        return DateTime.Parse(value);
    }
}

And then finally register an instance with GlobalConfiguration in Global.asax.cs

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new DateTimeToStringJsonConverter());

Any thoughts/ideas/opinions? Thanks!


EDIT: 2018.10.16 - Updated code to handle value types as the source type and to copy serializer settings over thanks to the following comment from the original SO answer:

NOTE: This solution is all over the internet, but has a flaw that manifests itself in rare occasions. The new JsonReader created in the ReadJson method does not inherit any of the original reader's configuration values (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling, etc...). These values should be copied over before using the new JsonReader in serializer.Populate().

Upvotes: 2

Joel Santos
Joel Santos

Reputation: 49

import this namespace System.Web.Script.Serialization;

string SerializeObject()
{
     var objs = new List<Test>()
     var objSerialized = new JavaScriptSerializer();
     return objSerialized .Serialize(objs);
}

I use as example a List but you will use your object instead.

Upvotes: 1

Related Questions