Search code examples
c#datetimestring-formattingdatacontractserializer

Serializing UTC time c#


I have a DateTime datamember in a datacontract. By default the datacontractserializer serializes the UTC time to yyyy-MM-ddTHH:mm:ss.fffffffZ format. I need it in yyyy-MM-ddTHH:mm:ss.000Z format but have no control over the datacontracts. So is there anything I can do with the DataContractSerializer that would give me the UTC time in the format I want. Thanks


Solution

  • I've created an implementation that uses an implementation of the IDataContractSurrogate to serialize your DTO's with DTO's you own.

    DTO's

    You didn't provide DTO's so I've created one to be your original DTO (that you can't change) and one replacement DTO that we own. They will have the same public signature, except their DateTime properties are changed to String types.

    /// <summary>
    /// original DTO, is fixed
    /// </summary>
    [DataContract]
    class DTO
    {
        [DataMember]
        public DateTime FirstDate { get; set; }
    
    }
    
    /// <summary>
    /// Our own DTO, will act as surrogate
    /// </summary>
    [DataContract(Name="DTO")]
    class DTO_UTC
    {
        [DataMember]
        public string FirstDate { get; set; }
    }
    

    IDataContractSurrogate

    The IDataContractSurrogate provides the methods needed to substitute one type for another during serialization and deserialization.

    I used simple reflection here. If you need better performance look into emiting generated code between the types or even generate the target types.

    public class DTOTypeSurrogate : IDataContractSurrogate
    {
        // this determines how you want to replace one type with the other
        public Type GetDataContractType(Type type)
        {
            if (type == typeof(DTO))
            {
                return typeof(DTO_UTC);
            }
            return type;
        }
    
        public object GetDeserializedObject(object obj, Type targetType)
        {
            // do we know this type
            if (targetType == typeof(DTO))
            {
                // find each DateTime prop and copy over
                var objType = obj.GetType();
                var target = Activator.CreateInstance(targetType);
                foreach(var prop in targetType.GetProperties())
                {
                    // value comes in
                    var src =  objType.GetProperty(prop.Name);
                    // do we need special handling
                    if (prop.PropertyType == typeof(DateTime))
                    {
                        DateTime utcConvert;
                        // parse to a datetime
                        if (DateTime.TryParse(
                            (string) src.GetValue(obj),
                            System.Globalization.CultureInfo.InvariantCulture, 
                            System.Globalization.DateTimeStyles.AdjustToUniversal, 
                            out utcConvert))
                        {
                            // store 
                            prop.SetValue(target, utcConvert);
                        }
                    }
                    else
                    {
                        // store non DateTime types
                        prop.SetValue(target, src);
                    }
                }
                return target;
            }
            return obj;
        }
    
        public object GetObjectToSerialize(object obj, Type targetType)
        {
            // go from DTO to DTO_UTC
            if (targetType == typeof(DTO_UTC))
            {
                var utcObj = Activator.CreateInstance(targetType);
                var objType = obj.GetType();
                // find our DateTime props
                foreach(var prop in objType.GetProperties())
                {
                    var src = prop.GetValue(obj);
                    if (prop.PropertyType == typeof(DateTime))
                    {
                        // create the string
                        var dateUtc = (DateTime)src;
                        var utcString = dateUtc.ToString(
                            "yyyy-MM-ddThh:mm:ss.000Z", 
                            System.Globalization.CultureInfo.InvariantCulture);
                        // store
                        targetType.GetProperty(prop.Name).SetValue(utcObj, utcString);
                    } else
                    {
                        // normal copy
                        targetType.GetProperty(prop.Name).SetValue(utcObj, src);
                    }
                }
                return utcObj;
            }
            // unknown types return the original obj
            return obj;
        }
       // omitted the other methods in the interfaces for brevity
    }
    

    Usage with the serializer

    Here we create the DataContractSerializer and provide it with an instantiated DTO and after serialization we reverse the process to check if the result is the same.

    var surrogateSerializer =
        new DataContractSerializer(
            typeof(DTO),
            new Type[] {}, 
            Int16.MaxValue, 
            false, 
            true, 
            new DTOTypeSurrogate()); // here we provide our own implementation
    
    var ms = new MemoryStream();
    // test data
    var testDto = new DTO { 
        FirstDate = new DateTime(2015, 12, 31, 4, 5, 6, DateTimeKind.Utc) };
    // serialize
    surrogateSerializer.WriteObject(ms, testDto);
    // debug
    var wireformat = Encoding.UTF8.GetString(ms.ToArray());
    
    //reset
    ms.Position = 0;
    //deserialize
    var dtoInstance = (DTO) surrogateSerializer.ReadObject(ms);
    // verify we have the same data returned
    Debug.Assert(dtoInstance.FirstDate == testDto.FirstDate);