.net JSON 或 XML 对象序列化和包含字段类型的创建

Posted

技术标签:

【中文标题】.net JSON 或 XML 对象序列化和包含字段类型的创建【英文标题】:.net JSON or XML object serialization and creation with field types inclued 【发布时间】:2014-10-27 04:18:17 【问题描述】:

我正在尝试将动态 DTO 映射到 JSON 或 XML,但要获取有关字段类型的信息。 所以我很想知道看起来像这样的对象是否有可能:

public class AddressDto
    
        public string Street  get; set; 
    
public class UserInfoDto
    
        public string UserName  get; set;             
        public int Age  get; set; 
        public AddressDto Address  get; set; 
    

要在 JSON(或 XML)中得到类似的东西:


    "fieldType": "UserInfoDto"
    "objectValue":
    
        
            "fieldType": "string",
            "fieldName": "UserName",
            "fieldValue": "John Doe",
        ,
        
            "fieldType": "integer",
            "fieldName": "Age",
            "fieldValue": "27",
        ,
        
            "fieldType": "AddressDto",
            "fieldName": "Address",
            "fieldValue": 
                "fieldType": "string",
                "fieldName": "Street",
                "fieldValue": "Lorem Ipsum"
            
        
    

...反之亦然。

【问题讨论】:

您希望自动执行此操作吗?因为我有一个手动解决方案(实际上很乏味)。 自动会更好。我可以通过手动使用反射来生成一些自定义 JSON 或 XML 来做到这一点,但是当我需要将其转换回源对象时遇到了一些问题。因此,如果您有任何解决方案,请随时分享。 【参考方案1】:

是的,这是可能的,但是您必须做一些工作来指导序列化程序如何格式化输出字符串。如果您想坚持使用内置的 .NET 序列化程序,可以使用 System.Runtime.Serialization.Json.DataContractJsonSerializer 类来实现。

1- 创建 MetadataObject 类作为输出数据的包装对象

定义如下类并用[DataContract]标记,这样就可以序列化了:

[DataContract]
public class MetadataObject

    [DataMember(Name = "fieldType")]
    public string FieldType  get; set; 

    [DataMember(Name = "fieldName")]
    public string FieldName  get; set; 

    [DataMember(Name = "fieldValue")]
    public object FieldValue  get; set; 

2- 告诉序列化器如何序列化父(UserInfoDto)对象

为此,您需要让您的UserInfoDto 对象实现ISerializable 接口(更具体地说,GetObjectData() 方法)并将其标记为[Serializable]。您还需要包含此类将包含的所有自定义类型。在这种情况下,它将是 AddressDto 类型,以及在 GetObjectData() 方法中构造的 List<MedataObject>。最后的类如下所示:

[KnownType(typeof(AddressDto))]
[KnownType(typeof(List<MetadataObject>))]
[Serializable]
public class UserInfoDto : ISerializable

    public string UserName  get; set; 

    public int Age  get; set; 

    public AddressDto Address  get; set; 

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    
        var nodes = this.GetType().GetProperties().Select(property =>
        
            return new MetadataObject
            
                FieldType = property.PropertyType.Name,
                FieldName = property.Name,
                FieldValue = property.GetValue(this, null)
            ;
        ).ToList();

        info.AddValue("fieldType", this.GetType().Name);
        info.AddValue("objectValue", nodes);
    

注意GetObjectData() 方法使用反射并为每个属性创建一个MetadataObject 类。这样做的美妙之处在于它使代码更加通用。所以以后如果你决定需要更多像UserInfoDto这样的类,你可以把这个逻辑放在一个基类中,让其他类继承它。

3- 告诉序列化器如何序列化子 (AddressDto) 对象

UserInfoDto,让子类实现ISerializable并将其标记为[Serializable]

[Serializable]
public class AddressDto : ISerializable

    public string Street  get; set; 

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    
        foreach (var property in this.GetType().GetProperties())
        
            info.AddValue("fieldType", property.PropertyType.Name);
            info.AddValue("fieldName", property.Name);
            info.AddValue("fieldValue", property.GetValue(this, null));
        
    

4- 将它们放在一起

最后,像这样定义你的序列化器:

DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(UserInfoDto));

var data = new UserInfoDto  Age = 30, UserName = "John" ;
data.Address = new AddressDto  Street = "123 ABC" ;

using (MemoryStream stream = new MemoryStream())

    using (StreamReader reader = new StreamReader(stream))
    
        serializer.WriteObject(stream, data);
        stream.Position = 0;

        var output = reader.ReadToEnd();
    

当你运行它时,output 看起来像这样:

  
   "fieldType":"UserInfoDto",
   "objectValue":[  
        
         "__type":"MetadataObject:#***.Console",
         "fieldName":"UserName",
         "fieldType":"String",
         "fieldValue":"John"
      ,
        
         "__type":"MetadataObject:#***.Console",
         "fieldName":"Age",
         "fieldType":"Int32",
         "fieldValue":30
      ,
        
         "__type":"MetadataObject:#***.Console",
         "fieldName":"Address",
         "fieldType":"AddressDto",
         "fieldValue":  
            "__type":"AddressDto:#***.Console",
            "fieldType":"String",
            "fieldName":"Street",
            "fieldValue":"123 ABC"
         
      
   ]

请注意,__type 属性是由序列化程序自动生成的。如果您使用的是 .NET 4.5,则可以try the following to have it not be part of the output(如果需要将字符串反序列化回对象,您可能会需要它们)

【讨论】:

这是一个不错的解决方案,但我需要它来处理我无法像这样修改的任何自定义 dto(我无法添加 [Serializable] 注释或任何其他更改)。它应该适用于通过第三方程序集反射加载的对象。 在这种情况下,您将需要定义自己的数据合约序列化程序,坦率地说,.NET Json 库会让您感到痛苦。看看 Json.NET。

以上是关于.net JSON 或 XML 对象序列化和包含字段类型的创建的主要内容,如果未能解决你的问题,请参考以下文章

Json/XML序列化和反序列化

.NET中常用的几种解析JSON方法

在VS中快速生成Json或XML代码

ASP.NET Core中如何使用缓存

如何使用 Json.NET 将 XML 序列化为 JSON 对象

JAVA序列化与反序列化三种格式存取(默认格式XML格式JSON格式)