DataContractJsonSerializer - десериализация DateTime в списке <object> - PullRequest
8 голосов
/ 13 февраля 2012

У меня проблемы с использованием класса System.Runtime.Serialization.Json.DataContractJsonSerializer для десериализации экземпляров DateTime, содержащихся в List<object>.Я не могу заставить DateTime десериализовать обратно в исходный тип.DataContractJsonSerializer всегда десериализует его в строковый тип в формате "/Date(1329159196126-0500)/".Он будет сериализован и десериализован нормально, если я пропущу его с использованием строго типизированного List<DateTime>, однако я ищу способ заставить сериализатор идентифицировать и правильно десериализовать DateTimes при обнаружении в простом списке или массиве object.

Обратите внимание, что DateTimes является единственным типом, кроме примитивов и строк, который этот список когда-либо будет содержать.Вот фрагмент кода, который я использую для проверки.

var list = new List<object> { 27, "foo bar", 12.34m, true, DateTime.Now };
var serializer = new DataContractJsonSerializer(typeof (List<object>));
using (MemoryStream ms = new MemoryStream())
{
    serializer.WriteObject(ms, list);
    ms.Position = 0;
    var deserializedList = serializer.ReadObject(ms) as List<object>;
}

Ответы [ 4 ]

33 голосов
/ 21 марта 2014

В .NET Framework версии 4.5 у DataContractJsonSerializer есть конструктор, который принимает объект DataContractJsonSerializerSettings, который можно использовать для установки DateTimeFormat:

var ser = new DataContractJsonSerializer(typeof(CreateOmsEntryCommand),
              new DataContractJsonSerializerSettings
              {
                  DateTimeFormat = new DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ")
              });
7 голосов
/ 19 февраля 2012

Это кажется очень странным поведением, я предполагаю, что оно происходит от DateTime, не являющегося типом, который распознается в JSON.Однако вы можете свернуть свой собственный IDataContractSurrogate, чтобы изменить процесс сериализации / десериализации.

Чтобы использовать это, измените ваш пример кода, когда вы создаете сериализатор к этому:

var serializer = new DataContractJsonSerializer(typeof(List<object>), null, int.MaxValue, false, new DateTimeDataContractSurrogate(), true);

Затем добавьте этокласс:

public class DateTimeDataContractSurrogate : IDataContractSurrogate
    {
        private static readonly Regex dateRegex = new Regex(@"/Date\((\d+)([-+])(\d+)\)/");
        private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        public object GetCustomDataToExport(Type clrType, Type dataContractType)
        {
            // not used
            return null;
        }

        public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
        {
            // not used
            return null;
        }

        public Type GetDataContractType(Type type)
        {
            // not used
            return type;
        }

        public object GetDeserializedObject(object obj, Type targetType)
        {
            // for debugging
            //Console.WriteLine("GetDeserializedObject: obj = {0} ({1}), targetType = {2}", obj, obj.GetType(), targetType);

            // only act on List<object> types
            if (obj.GetType() == typeof(List<object>))
            {
                var objList = (List<object>)obj;

                List<object> copyList = new List<object>(); // a list to copy values into. this will be the list returned.
                foreach (var item in objList)
                {
                    string s = item as string;
                    if (s != null)
                    {
                        // check if we match the DateTime format
                        Match match = dateRegex.Match(s);
                        if (match.Success)
                        {
                            // try to parse the string into a long. then create a datetime and convert to local time.
                            long msFromEpoch;
                            if (long.TryParse(match.Groups[1].Value, out msFromEpoch))
                            {
                                TimeSpan fromEpoch = TimeSpan.FromMilliseconds(msFromEpoch);
                                copyList.Add(TimeZoneInfo.ConvertTimeFromUtc(epoch.Add(fromEpoch), TimeZoneInfo.Local));
                                continue;
                            }
                        }
                    }

                    copyList.Add(item); // add unmodified
                }

                return copyList;
            }

            return obj;
        }

        public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
        {
            // not used   
        }

        public object GetObjectToSerialize(object obj, Type targetType)
        {
            // for debugging
            //Console.WriteLine("GetObjectToSerialize: obj = {0} ({1}), targetType = {2}", obj, obj.GetType(), targetType);
            return obj;
        }

        public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
        {
            // not used
            return null;
        }

        public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
        {
            // not used
            return typeDeclaration;
        }
    }
5 голосов
/ 13 февраля 2012

Если DataContractJsonSerializer не является обязательным , вот решение с использованием Json.Net .

var list = new List<object> { 27, "foo bar", 12.34m, true, DateTime.Now };

string json = JsonConvert.SerializeObject(list);
var orgObj=JsonConvert.DeserializeObject<List<object>>(json);

Это строка Json

[27,"foo bar",12.34,true,"\/Date(1329161615596+0200)\/"]

и возвращаемые типы: long, string, double, bool и DateTime

3 голосов
/ 16 февраля 2012

Вы можете преобразовать DateTime.Now в строку перед сериализацией и
преобразовать его обратно в DateTime после десериализации.

Преобразование в строку по:

string dateAsString = Convert.ToString(DateTime.Now);

Преобразование обратно в DateTime после десериализации:

DateTime dateTime = Convert.ToDateTime(deserializedList[4]);

Таким образом, весь код будет выглядеть так:

  string dateAsString = Convert.ToString(DateTime.Now);
  var list = new object[] { 27, "foo bar", 12.34m, true, dateAsString };

  var serializer = new DataContractJsonSerializer(typeof (List<object>));

  using (MemoryStream ms = new MemoryStream())
  {
    serializer.WriteObject(ms, list);
    ms.Position = 0;
    var deserializedList = serializer.ReadObject(ms) as List<object>;
    DateTime dateTime = Convert.ToDateTime(deserializedList[4]);
  }
...