Convert long number as string in the serialization

2020-02-09 03:18发布

问题:

I have a custom made class that use a long as ID. However, when I call my action using ajax, my ID is truncated and it loses the last 2 numbers because javascript loses precision when dealing with large numbers. My solution would be to give a string to my javascript, but the ID have to stay as a long on the server side.

Is there a way to serialize the property as a string? I'm looking for some kind of attribute.

Controller

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 Result

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

回答1:

You could probably create a custom JsonConverter and apply it on your property.

Following is an example (NOTE: I haven't used this api before so it could probably be improved more, but following should give you a rough idea):

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());
    }
}

Web API Action:

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

Request:

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"}

Response:

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"}

EDIT:
if you do not want to decorate the property with an attribute, you could instead add it to the Converters collection. Example:

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


回答2:

How about a view model that only has string properties, like this:

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

Now you are only dealing with strings and JSON serialization problems go away.



回答3:

I would probably just create an anonymous type when I call the json serializer like;

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

In the case where your class has 20 or something fields and this becomes a very ugly solution I would add a string to the class called ID, change the long to lID or something and use the serializer settings to ignore the long when serializing so the resulting json will only have the string version.

There are several different attributes that will accomplish this for example;

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

Will not serialize _ID



回答4:

Depending on your case, you could potentially use getters and setters to masquerade the property as a string during JSON serialization.

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); }
    }
}


回答5:

Maybe a viable workaround would be to use another type for the ID? You could use a GUID for example. With a GUID you will never run out of bounds and also this will get serialized as a string by default I think.