Преобразование длинного числа в строку в сериализации

У меня есть пользовательский класс, который использует длинный идентификатор. Однако, когда я вызываю свое действие с помощью ajax, мой идентификатор усекается, и он теряет последние 2 числа, потому что javascript теряет точность при работе с большими числами. Мое решение состояло в том, чтобы указать строку на мой javascript, но идентификатор должен оставаться длинным на стороне сервера.

Есть ли способ сериализации свойства как строки? Я ищу какой-то атрибут.

контроллер

public class CustomersController : ApiController
{
   public IEnumerable<CustomerEntity> Get()
   {
      yield return new CustomerEntity() { ID = 1306270928525862486, Name = "Test" };
   }
}

Model

public class CustomerEntity
{
   public long ID { get; set; }
   public string Name { get; set; }
}

Результат JSON

[{"Name":"Test","ID":1306270928525862400}]

Ответ 1

Возможно, вы можете создать пользовательский JsonConverter и применить его к своему свойству.

Ниже приведен пример ( ПРИМЕЧАНИЕ: я не использовал этот api раньше, чтобы его можно было улучшить больше, но следующее должно дать вам приблизительную идею):

public class Person
{
    [JsonConverter(typeof(IdToStringConverter))]
    public long ID { get; set; }

    public string Name { get; set; }
}

public class IdToStringConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken jt = JValue.ReadFrom(reader);

        return jt.Value<long>();
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(System.Int64).Equals(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value.ToString());
    }
}

Действие веб-API:

public Person Post([FromBody]Person person)
{
    return person;
}

Запрос:

POST http://asdfasdf/api/values HTTP/1.1  
Host: servername:9095  
Connection: Keep-Alive  
Content-Type: application/json  
Content-Length: 42  

{"ID":"1306270928525862400","Name":"Mike"}

Ответ:

HTTP/1.1 200 OK  
Content-Length: 42  
Content-Type: application/json; charset=utf-8  
Server: Microsoft-HTTPAPI/2.0  
Date: Fri, 28 Jun 2013 17:02:18 GMT  

{"ID":"1306270928525862400","Name":"Mike"}

ИЗМЕНИТЬ:
если вы не хотите украшать свойство атрибутом, вы можете добавить его в коллекцию Converters. Пример:

config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new IdToStringConverter());

Ответ 2

Как насчет view model, который имеет только string свойства, например:

public class CustomerEntityViewModel
{
    public string ID { get; set; }
    public string Name { get; set; }
}

Теперь вы имеете дело только с string и JSON проблемами сериализации.

Ответ 3

Я бы просто создал анонимный тип, когда я вызываю сериализатор json, например:

 JsonConvert.SerializeObject(new { instance.Name, instance.ID.ToString() } );

В том случае, если у вашего класса есть 20 или что-то такое, и это становится очень уродливым решением, я бы добавил строку в класс с именем ID, изменил long на lID или что-то подобное и использовал настройки сериализатора для игнорирования длинный при сериализации, поэтому полученный json будет иметь только строчную версию.

Существует несколько различных атрибутов, которые будут выполняться, например:

 public class CustomerEntity
 {
      [ScriptIgnore]
      public long _ID { get; set; }
      public string ID { get; set; }
      public string Name { get; set; }
 }

Не будет сериализоваться _ID

Ответ 4

Возможно, жизнеспособным решением будет использование другого типа для ID? Например, вы можете использовать GUID. С GUID вы никогда не закончите границы, и, по-моему, это будет сериализоваться как строка по умолчанию.

Ответ 5

В зависимости от вашего случая вы могли бы использовать геттеры и сеттеры для маскировки свойства как строки во время сериализации JSON.

public class Money
{
    [JsonIgnore]
    public decimal Money { get; set; }

    [JsonProperty("money")]
    public string MoneyAsString
    {
        get { return Money.ToString("0.00"); }
        set { Money = decimal.Parse(value); }
    }
}