Не работает кастомный JsonConverter

168
22 апреля 2018, 20:44

Имеется json:

{
    "access_token": "xxx",
    "token_type": "bearer",
    "expires_in": 86400,
    "refresh_token": "yyy",
    "created_at": 1524344276
}

created_at возвращает время в секундах от 1970 года, поэтому модель для этого json написал следующую:

public class OAuth2Token
{
    [JsonProperty("access_token")]
    public string AccessToken  { get; }
    [JsonProperty("token_type")]
    public string TokenType    { get; }
    [JsonProperty("expires_in")]
    public int    ExpiresIn    { get; } //Seconds
    [JsonProperty("refresh_token")]
    public string RefreshToken { get; }
    [JsonProperty("created_at"), JsonConverter(typeof(SecondEpochConverter))]
    public DateTime CreatedAt { get; }
}

И для конвертации секунд в DateTime использую конвертер:

public class SecondEpochConverter : DateTimeConverterBase
{
    private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(((DateTime)value - _epoch).TotalSeconds.ToString());
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) { return null; }
        return _epoch.AddSeconds((long)reader.Value);
    }
}

Однако при попытке распарсить срабатывает исключение:

JsonReaderException: Unexpected character encountered while parsing value: 1. Path 'created_at', line 1, position 221.

UPD:

Для работы с автоматическими свойствами без сеттеров установлена следующая настройка:

jsonSerializerSettings = new JsonSerializerSettings //for (de-)serialization get-autoproperty
{
    ContractResolver = new PrivateSetterContractResolver()
};

(Github и NuGet решения) и вызывается десериализация вот так:

//T - в данном случае OAuth2Token
JsonConvert.DeserializeObject<T>(response, jsonSerializerSettings);
Answer 1

Вы забыли сеттеры.

Рабочий пример:

void Main()
{
    var source = @"{
    ""access_token"": ""xxx"",
    ""token_type"": ""bearer"",
    ""expires_in"": 86400,
    ""refresh_token"": ""yyy"",
    ""created_at"": 1524344276
}";
    var result = JsonConvert.DeserializeObject<OAuth2Token>(source);
    result.Dump();
}
// Define other methods and classes here
public class SecondEpochConverter : JsonConverter
{
    private static readonly DateTime _epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(((DateTime)value - _epoch).TotalSeconds.ToString());
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null) { return null; }
        return _epoch.AddSeconds((long)reader.Value);
    }
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}
public class OAuth2Token
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }
    [JsonProperty("token_type")]
    public string TokenType { get; set; }
    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; } //Seconds
    [JsonProperty("refresh_token")]
    public string RefreshToken { get; set; }
    [JsonProperty("created_at")]
    [JsonConverter(typeof(SecondEpochConverter))]
    public DateTime CreatedAt { get; set; }
}

Вывод:

(Если заменить JsonConverter обратно на DateTimeConverterBase и убрать CanConvert - тоже будет работать)

С учётом вскрывшихся подробностей. Посмотрите пример:

var settings = new JsonSerializerSettings  
{
    ContractResolver = new PrivateSetterContractResolver()
};
var model = JsonConvert.DeserializeObject<Model>(json, settings);  

где модель:

public class Model  
{
    public string Value { get; private set; }
}

That's it.

PS Можно поставить только на одно это свойство с кастомным конвертером.

READ ALSO
Быстрое сложение изображений в C#

Быстрое сложение изображений в C#

Основная задача: мне необходимо в реальном времени (или около того) сложение двух Grayscale FullHD 8bit изображений (которые будут использованы как...

199
Unity Coroutines C#

Unity Coroutines C#

Как сделать универсальную корутину для запуска какой то функции через определённое время?

216
Смысл помещения DateTime в контейнер

Смысл помещения DateTime в контейнер

В исходном коде одной программы, внутри NinjectModule, я нашёл такую строку:

176
Бегущая пунктирная рамка у блока

Бегущая пунктирная рамка у блока

Как сделать так, чтобы граница блока была не статичной, а бегала по направлению часовой стрелки? Понимаю, что как-то должно быть с помощью...

217