将 JSON 包反序列化为具有自定义属性名称的类

Posted

技术标签:

【中文标题】将 JSON 包反序列化为具有自定义属性名称的类【英文标题】:Deserializing a JSON package to a class with custom property names 【发布时间】:2021-06-06 07:33:42 【问题描述】:

问题

我从服务器接收到格式为 "A": 'Pelle', "B": 55, "C": 5.5 的 JSON 包,需要将其映射到以下格式的实体类:

class EntityAttributes

  public string Name  get; set; 
  public int Level  get; set; 
  public float Strength  get; set; 

JSON中的属性名“A”、“B”、“C”应按照如下类进行映射:

class Constants

  public const byte NAME = 65;     // ASCII "A"
  public const byte LEVEL = 66;    // ASCII "B"
  public const byte STRENGTH = 67; // ASCII "C"

其中 65 是“A”的 ASCII 字节表示,“B”的 66 和“C”的 67。

我被困在哪里(我不知道这是否是正确的方法):

我一直在尝试使用JSON NET FOR UNITY,它允许您使用自定义名称将 JSON 反序列化为一个类:

class EntityAttributes

  [[JsonProperty("A")]]
  public string Name  get; set; 
  [[JsonProperty("B")]]
  public int Level  get; set; 
  [[JsonProperty("C")]]
  public float Strength  get; set; 

但是,我不想在注释中硬编码“A”、“B”和“C”,因为这些将来可能会发生变化。也不可能(afaik)在装饰器中从字节转换为字符串,因为只允许 常量表达式

知道我应该如何解决这个问题吗?

【问题讨论】:

为什么你的常量不能简单地是字符串? 常量的服务器内部表示是字节。必须同步服务器常量和客户端常量已经有点麻烦了,在此之上添加一个字节 -> 字符转换并不理想。 为什么服务器常量不能是字符串? / 为什么服务端不能有常量字节和字符串之间的非常量映射? 也许可以,但是改变内部服务器结构,使常量必须匹配客户端内部结构的方法名称真的有意义吗? 好吧,在一方面,你必须这样做..要么服务器必须提供客户端所期望的客户端,要么必须处理服务器提供的任何东西......无论哪种方式,如果一个一边改变了,另一边也必须改变。可能没有办法解决这个问题 【参考方案1】:

这听起来有点像主要问题是您的字段名称必须保持不变 - 您不想要什么。

由于您的属性足够简单/基本,您可以改用很久以前由 Units 社区中的某个人编写的 SimpleJson;)您只需在项目的任何位置创建该文件。

最大的优势:它可以使用完全动态的字段名称,因此您可以轻松地制作它,例如

public static class Constants

    public const byte NAME = 65;
    public const byte LEVEL = 66;
    public const byte STRENGTH = 67;        

然后做例如

class EntityAttributes

    public readonly string Name get;set;;
    public readonly int Level get;set;
    public readonly float Strength get;set;

    public EntityAttributes(string name, int level, float strength)
    
        Name = name;
        Level = level;
        Strength = strength;
    

现在,无论您最初将 JSON 直接解析为哪种类型,您都不会再使用该类型了。相反,你会做类似的事情,例如

var root = JSON.Parse(jsonString);
var item = new EntityAttributes (
    root[Encoding.ASCII.GetString(new [](Constants.NAME)].Value, 
    root[Encoding.ASCII.GetString(new []Constants.LEVEL)].AsInt(),
    root[Encoding.ASCII.GetString(new []Constants.STRENGTH)].AsFloat() 
);

请注意,当然,无论哪种方式,您仍然必须正确设置这些常量字节值。

所以在我个人看来,我仍然认为你可以/应该简单地使用

public static class Constants

    public const string NAME = "A";
    public const string LEVEL = "B";    
    public const string STRENGTH = "C";

那你就不会有那个麻烦了。您的服务器必须提供客户端期望的任何结构,或者客户端必须处理服务器提供的任何结构。无论哪种方式,如果一侧发生更改,另一侧也必须更改。


注意:在智能手机上输入,但我希望思路清晰

【讨论】:

【参考方案2】:

您可以创建一个自定义JsonConverter 来解决这个问题:

class EntityAttributesConverter : JsonConverter

    public override bool CanConvert(Type objectType)
    
        return objectType == typeof(EntityAttributes);
    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        JObject jo = JObject.Load(reader);
        return new EntityAttributes
        
            Name = GetValue<string>(jo, Constants.NAME),
            Level = GetValue<int>(jo, Constants.LEVEL),
            Strength = GetValue<float>(jo, Constants.STRENGTH)
        ;
    

    private static T GetValue<T>(JObject jo, byte b)
    
        JToken val = jo[Encoding.ASCII.GetString(new byte[]  b )];
        return val != null ? val.ToObject<T>() : default(T);
    

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    
        throw new NotImplementedException();
    

然后只需像这样用[JsonConverter] 属性标记您的EntityAttributes 类:

[JsonConverter(typeof(EntityAttributesConverter))]
class EntityAttributes

    public string Name  get; set; 
    public int Level  get; set; 
    public float Strength  get; set; 

然后像往常一样反序列化:

var attributes = JsonConvert.DeserializeObject<EntityAttributes>(json);

这是一个工作演示(在控制台应用程序中):https://dotnetfiddle.net/VPfbdy

【讨论】:

【参考方案3】:

如果需要字节,请使用 JsonUtility.FromJson 反序列化 JSON 文件并简单转换为 System.Text.Encoding.UTF8.GetBytes(myString)。

【讨论】:

是的,这不是问题..问题基本上是如何拥有动态字段名称

以上是关于将 JSON 包反序列化为具有自定义属性名称的类的主要内容,如果未能解决你的问题,请参考以下文章

ASP Web API:将对象序列化为 JSON 时指定自定义字段名称

将objective-c自定义对象序列化为OSX的JSON?

Scala:将case类序列化为JSON,备用名称

在 Ktor 中指定要序列化为 JSON 的类字段

如何将具有嵌套属性的 JSON 对象反序列化为 Symfony 实体?

将 JSON 反序列化为 C# 类,其中 JSON 中的属性名称是动态的