【问题标题】:Convert long number as string in the serialization在序列化中将长数字转换为字符串
【发布时间】:2013-06-26 11:55:26
【问题描述】:

我有一个使用 long as ID 的定制类。但是,当我使用 ajax 调用我的操作时,我的 ID 被截断并且丢失了最后 2 个数字,因为 javascript 在处理大数字时会丢失精度。我的解决方案是为我的 javascript 提供一个字符串,但 ID 必须在服务器端保持很长。

有没有办法将属性序列化为字符串?我正在寻找某种属性。

控制器

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

型号

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

JSON 结果

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

【问题讨论】:

    标签: c# json asp.net-mvc-4 asp.net-web-api


    【解决方案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());
        }
    }
    

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

    【讨论】:

    • 谢谢,太好了!该答案的唯一缺点是我们的实体继承自我们的 ORM 中的自定义实体模板,该模板用于我们拥有的每个项目(其中一些不是基于 Web 的)。这意味着将 Newtonsoft.JSON 的依赖项添加到 ORM,并且我们拥有的每个项目也需要 newtonsoft。我更喜欢不依赖于 DLL 的属性。
    • @Bruno:有一件事要警告你。该转换器会将所有长整数转换为字符串。您可能应该研究一些方法来进行一些检查,以便它不适用于应用程序范围以及任何性能增强,以便除了您期望的类型之外不触发此转换器,例如:CustomerEntity
    • 我们很少使用 long 来表示不是 ID 的东西
    • 不错的解决方案 - 值得一提的是它不处理 long? (Nullable&lt;long&gt;),这有时也很有用。
    • 关于“编辑”——如果有人遇到同样的问题——它不想为我工作,因为stackoverflow.com/questions/24620110/…
    【解决方案2】:

    只有string 属性的view model 怎么样,像这样:

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

    现在您只需处理strings 和JSON 序列化问题就消失了。

    【讨论】:

    • 我认为他想在服务器端保持很长时间
    • @cinek - 他可以,因为他可以将 CustomerEntity 模型类转换为 CustomerEntityViewModel 并将其传递给客户端,然后在返回服务器时可以将其转换回CustomerEntity 类,ID 又是 long。我同意它正在跳跃,但如果需要这种精度,那么需要在某个地方进行一些到 string 的转换。
    • 这意味着为我的每个实体创建一个视图模型(因为它们都使用长 ID)并复制每个实体中的每个属性
    • @Bruno - 可悲的是,这就是我的意思。 :-(
    • 这种方法的问题是(在某些情况下,没有专门讨论这种情况)字符串的默认值与 int/long/etc' 不同。如果这些模型被用于向期望数字或零的端点发出请求,但随后得到一个空字符串,则它可能会失败或呈现不稳定的行为。这也是可以解决的,但是在一个地方创建一个 JSON 转换器并解决问题会更安全
    【解决方案3】:

    当我调用 json 序列化程序时,我可能只是创建一个匿名类型;

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

    如果你的类有 20 个或其他一些字段,这会变成一个非常丑陋的解决方案,我会向名为 ID 的类添加一个字符串,将 long 更改为 lID 或其他东西,并使用序列化程序设置忽略序列化时的 long ,因此生成的 json 将只有字符串版本。

    例如,有几个不同的属性可以实现这一点;

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

    不会序列化_ID

    【讨论】:

      【解决方案4】:

      根据您的情况,您可能会在 JSON 序列化期间使用 getter 和 setter 将属性伪装成字符串。

      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】:

        也许一个可行的解决方法是使用另一种类型的 ID?例如,您可以使用 GUID。使用 GUID,您将永远不会超出范围,而且我认为默认情况下这将被序列化为字符串。

        【讨论】:

        • 我无法更改类型,但我正在寻找一种方法来序列化一个长字符串,如 GUID
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-13
        • 2010-12-25
        相关资源
        最近更新 更多