2014-09-02 141 views
3

我試圖將動態DTO映射到JSON或XML,但有關於字段類型的信息。 所以我有興趣瞭解是否有可能從物體看起來看起來這樣:.net 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" 
      } 
     } 
    } 
} 

...和副反之亦然。

+0

你想這是自動的嗎?因爲我有一個手動的解決方案(實際上很乏味)。 – 2014-09-02 21:05:30

+0

自動會更好。我可以通過手動使用反射來生成一些自定義JSON或XML,但是當我需要將它轉換回源對象時,我遇到了一些問題。所以如果你有任何解決方案可以隨意分享。 – ShP 2014-09-03 09:24:38

回答

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對象ct執行ISerializable接口(更具體地說,GetObjectData()方法)並將其標記爲[Serializable]。您還需要包含此類將包含的所有自定義類型。在這種情況下,它將是AddressDto類型以及List<MedataObject>,它將在GetObjectData()方法中構建。最終的類如下所示:

[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:#StackOverflow.Console", 
     "fieldName":"UserName", 
     "fieldType":"String", 
     "fieldValue":"John" 
     }, 
     { 
     "__type":"MetadataObject:#StackOverflow.Console", 
     "fieldName":"Age", 
     "fieldType":"Int32", 
     "fieldValue":30 
     }, 
     { 
     "__type":"MetadataObject:#StackOverflow.Console", 
     "fieldName":"Address", 
     "fieldType":"AddressDto", 
     "fieldValue":{ 
      "__type":"AddressDto:#StackOverflow.Console", 
      "fieldType":"String", 
      "fieldName":"Street", 
      "fieldValue":"123 ABC" 
     } 
     } 
    ] 
} 

注意,__type屬性將自動通過串行產生的。如果你使用.NET 4。5,你可以try the following to have it not be part of the output(如果字符串需要反序列化回一個對象,你很有可能需要它們)

+0

這是一個很好的解決方案,但我需要這個來處理任何自定義dto,我不能像這樣修改(我不能添加[Serializable]插入或任何其他更改)。它應該通過從第三方程序集反射加載的對象來工作。 – ShP 2014-09-03 09:21:44

+0

在這種情況下,您將需要定義您自己的數據合約序列化程序,坦率地說,.NET Json庫會讓您感到痛苦。看看Json.NET。 – PoweredByOrange 2014-09-03 14:39:08

相關問題