2013-04-08 28 views
44

tôi muốn serialize mã này qua json.net:JSON.NET - làm thế nào để deserialize bộ sưu tập của trường hợp giao diện?

public interface ITestInterface 
{ 
    string Guid {get;set;} 
} 

public class TestClassThatImplementsTestInterface1 
{ 
    public string Guid { get;set; } 
} 

public class TestClassThatImplementsTestInterface2 
{ 
    public string Guid { get;set; } 
} 


public class ClassToSerializeViaJson 
{ 
    public ClassToSerializeViaJson() 
    {    
     this.CollectionToSerialize = new List<ITestInterface>(); 
     this.CollectionToSerialize.add(new TestClassThatImplementsTestInterface2()); 
     this.CollectionToSerialize.add(new TestClassThatImplementsTestInterface2()); 
    } 
    List<ITestInterface> CollectionToSerialize { get;set; } 
} 

Tôi muốn serialize/deserialize ClassToSerializeViaJson với json.net. Tính năng tuần tự hóa đang hoạt động, nhưng quá trình deserialization cho tôi lỗi này:

Newtonsoft.Json.JsonSerializationException: Không thể tạo một thể hiện kiểu ITestInterface. Kiểu là một giao diện hoặc lớp trừu tượng và không thể được khởi tạo.

Vậy làm thế nào tôi có thể deserialize bộ sưu tập Danh sách?

Cảm ơn

+0

Bạn đã thử những gì? Thậm chí bạn đã đọc tài liệu về JSON.NET chưa ?! Tôi khá chắc chắn serialization và deserialisation là một trong những điều đầu tiên tài liệu như vậy sẽ bao gồm. – Clint

+0

Có Serialization đang hoạt động nhưng tôi gặp lỗi khi tôi cố gắng deserialize: Newtonsoft.Json.JsonSerializationException: Không thể tạo một thể hiện kiểu ITestInterface. Kiểu là một giao diện hoặc lớp trừu tượng và không thể được khởi tạo. – user1130329

+2

sau đó có lẽ bạn nên dẫn đầu với điều đó trong câu hỏi của bạn? Thay vì yêu cầu mở như vậy "làm thế nào tôi có thể?" "Nó không làm việc" câu hỏi, bạn thực sự cần phải cung cấp tất cả các thông tin, những gì lỗi là nó? Nó đang diễn ra ở đâu? Những gì bạn đã cố gắng sửa chữa nó cho đến nay? Vui lòng chỉnh sửa câu hỏi của bạn bằng những thứ đó để cộng đồng có thể giúp bạn tốt hơn thay vì gắn cờ các câu hỏi của bạn. – Clint

Trả lời

27

Bellow đầy đủ ví dụ làm việc với những gì bạn muốn làm:

public interface ITestInterface 
{ 
    string Guid { get; set; } 
} 

public class TestClassThatImplementsTestInterface1 : ITestInterface 
{ 
    public string Guid { get; set; } 
    public string Something1 { get; set; } 
} 

public class TestClassThatImplementsTestInterface2 : ITestInterface 
{ 
    public string Guid { get; set; } 
    public string Something2 { get; set; } 
} 

public class ClassToSerializeViaJson 
{ 
    public ClassToSerializeViaJson() 
    { 
     this.CollectionToSerialize = new List<ITestInterface>(); 
    } 
    public List<ITestInterface> CollectionToSerialize { get; set; } 
} 

public class TypeNameSerializationBinder : SerializationBinder 
{ 
    public string TypeFormat { get; private set; } 

    public TypeNameSerializationBinder(string typeFormat) 
    { 
     TypeFormat = typeFormat; 
    } 

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName) 
    { 
     assemblyName = null; 
     typeName = serializedType.Name; 
    } 

    public override Type BindToType(string assemblyName, string typeName) 
    { 
     var resolvedTypeName = string.Format(TypeFormat, typeName); 
     return Type.GetType(resolvedTypeName, true); 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication"); 
     var toserialize = new ClassToSerializeViaJson(); 

     toserialize.CollectionToSerialize.Add(
      new TestClassThatImplementsTestInterface1() 
      { 
       Guid = Guid.NewGuid().ToString(), Something1 = "Some1" 
      }); 
     toserialize.CollectionToSerialize.Add(
      new TestClassThatImplementsTestInterface2() 
      { 
       Guid = Guid.NewGuid().ToString(), Something2 = "Some2" 
      }); 

     string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented, 
      new JsonSerializerSettings 
      { 
       TypeNameHandling = TypeNameHandling.Auto, 
       Binder = binder 
      }); 
     var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json, 
      new JsonSerializerSettings 
      { 
       TypeNameHandling = TypeNameHandling.Auto, 
       Binder = binder 
      }); 

     Console.ReadLine(); 
    } 
} 
+0

Điều này hoạt động hoàn hảo. Cảm ơn nhiều! – user1130329

+0

Bạn có thể sử dụng bên trong các chất kết dính FullyQualifiedName thay vì tên và bạn sẽ không phải vượt qua TypeFormat –

+0

Thực sự giúp đỡ trong vấn đề của tôi quá !!! Cảm ơn rất nhiều –

8

Sử dụng cài đặt mặc định, bạn không thể. JSON.NET không có cách nào để biết cách deserialize một mảng. Tuy nhiên, bạn có thể chỉ định trình chuyển đổi loại nào sẽ sử dụng cho loại giao diện của bạn. Để xem làm thế nào để làm điều này, xem trang này: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/

Bạn cũng có thể tìm kiếm thông tin về vấn đề này tại SO câu hỏi này: Casting interfaces for deserialization in JSON.NET

+0

Blog được đề cập ở đây là có giải thích chi tiết hơn. upvote cho erik –

71

Tôi đã tìm thấy câu hỏi này trong khi cố tự làm điều này. Sau khi tôi triển khai Garath's answer, tôi bị ấn tượng bởi vẻ đơn giản của nó. Nếu tôi chỉ đơn thuần triển khai một phương thức đã được truyền chính xác Type (như một chuỗi) mà tôi muốn khởi tạo, tại sao thư viện không ràng buộc nó một cách tự động?

Tôi thực sự thấy rằng tôi không cần bất kỳ chất kết dính tùy chỉnh nào, Json.Net có thể thực hiện chính xác những gì tôi cần, miễn là tôi đã nói đó là những gì tôi đang làm.

Khi serializing:

string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings 
{ 
    TypeNameHandling = TypeNameHandling.Objects, 
    TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple 
}); 

Khi de-serializing:

var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings 
{ 
    TypeNameHandling = TypeNameHandling.Objects 
}); 

tài liệu liên quan: Serialization Settings for Json.NETTypeNameHandling setting

+7

Điều này làm việc, nhưng mọi người nên hiểu điều này có thể sưng lên kích thước của thời gian lớn json của bạn. Đối với tôi, nó tăng kích thước tập tin đầu ra lên hơn 10X. –

+2

Lưu ý rằng việc đặt tên loại bê tông trong JSON kết quả có thể được coi là chi tiết triển khai rò rỉ và chắc chắn không sạch nếu JSON được sử dụng ở bất kỳ đâu ngoài mã của riêng bạn. Ngoài ra, nếu JSON bạn đang deserializing đến từ nguồn bên ngoài, hy vọng nó bao gồm tên loại của bạn là không hợp lý. –

+1

Với giải pháp này, người ta nên khử trùng các loại đến để tránh nguy cơ bảo mật tiềm ẩn. Xem [TypeNameHandling thận trọng trong Newtonsoft Json] (https://stackoverflow.com/q/39565954/3744182) để biết chi tiết. – dbc

13

Tôi cũng đã rất ngạc nhiên bởi sự đơn giản trong Garath, và cũng đã đến kết luận rằng thư viện Json có thể tự động làm điều đó. Nhưng tôi cũng thấy rằng nó thậm chí còn đơn giản hơn câu trả lời của Ben Jenkinson (mặc dù tôi có thể thấy nó đã được sửa đổi bởi chính nhà phát triển của thư viện json). Từ xét nghiệm của tôi, tất cả các bạn cần làm là thiết lập TypeNameHandling Auto, như thế này:

var objectToSerialize = new List<IFoo>(); 
// TODO: Add objects to list 
var jsonString = JsonConvert.SerializeObject(objectToSerialize, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 
var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 
+1

Nó là một vài năm sau khi bài gốc, nhưng đây là một cập nhật và cách làm việc của nó. Giá bạn trả là các tên loại được xuất dưới dạng thuộc tính "$ type" trên mỗi đối tượng trong JSON, nhưng điều đó là tốt trong nhiều trường hợp. – Grubl3r

+0

Đây là giải pháp đúng, cũng cho các cấu trúc dữ liệu phức tạp hơn. Đã cho tôi theo nghĩa đen ngày để tìm thấy nó mặc dù ... Bạn có thể thêm vào các thiết lập 'TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Đơn giản' để rút ngắn đầu ra kiểu càng nhiều càng tốt – metafa

+0

Với giải pháp này, người ta cũng nên khử trùng các loại đầu vào để tránh nguy cơ bảo mật tiềm ẩn. Xem [TypeNameHandling thận trọng trong Newtonsoft Json] (https://stackoverflow.com/q/39565954/3744182) để biết chi tiết. – dbc

3

Gần-bản sao của một câu trả lời Inrego, nhưng nó xứng đáng với giải thích thêm:

Nếu bạn sử dụng TypeNameHandling.Auto sau đó nó chỉ bao gồm tên loại/lắp ráp khi nó cần (tức là các giao diện và các lớp cơ sở/có nguồn gốc). Vì vậy, JSON của bạn sạch hơn, nhỏ hơn, cụ thể hơn.

Đó không phải là một trong những điểm bán hàng chính của nó trên XML/SOAP?

4

Đây là một câu hỏi cũ, nhưng nghĩ rằng tôi muốn thêm một sâu sắc hơn câu trả lời (theo hình thức một bài báo tôi đã viết): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/

TLDR: Thay vì cấu hình Json.NET nhúng gõ tên trong JSON tuần tự hóa, bạn có thể sử dụng trình chuyển đổi JSON để tìm ra lớp nào để deserialize sử dụng bất kỳ logic tùy chỉnh nào bạn thích.

Điều này có lợi thế là bạn có thể cấu trúc lại các loại của mình mà không phải lo lắng về việc phá vỡ quá trình giải tuần tự hóa.

2

Tôi muốn deserialize JSON đã không được đăng bởi ứng dụng của tôi, do đó tôi cần phải xác định việc thực hiện cụ thể bằng tay. Tôi đã mở rộng câu trả lời của Nicholas.

phép nói rằng chúng tôi có

public class Person 
{ 
    public ILocation Location { get;set; } 
} 

và các trường hợp cụ thể của

public class Location: ILocation 
{ 
    public string Address1 { get; set; } 
    // etc 
} 

Thêm trong lớp này

public class ConfigConverter<I, T> : JsonConverter 
{ 
    public override bool CanWrite => false; 
    public override bool CanRead => true; 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(I); 
    } 
    public override void WriteJson(JsonWriter writer, 
     object value, JsonSerializer serializer) 
    { 
     throw new InvalidOperationException("Use default serialization."); 
    } 

    public override object ReadJson(JsonReader reader, 
     Type objectType, object existingValue, 
     JsonSerializer serializer) 
    { 
     var jsonObject = JObject.Load(reader); 
     var deserialized = (T)Activator.CreateInstance(typeof(T)); 
     serializer.Populate(jsonObject.CreateReader(), deserialized); 
     return deserialized; 
    } 
} 

Sau đó xác định giao diện của bạn với JsonConverter thuộc tính

public class Person 
{ 
    [JsonConverter(typeof(ConfigConverter<ILocation, Location>))] 
    public ILocation Location { get;set; } 
} 
Các vấn đề liên quan