2013-05-13 131 views
3

我的问题是我想反序列化JSON对象到C#对象,但诀窍是,C#对象包含列表<抽象类>这个抽象类是一个超类的另有10个班。反序列化JSON对象,以多态C#对象,而typeNameHandling

public sealed class SearchAPIResult 
{ 
    public string Status; 

    public SearchAPIQuery Query; 

    public SearchResults Result; 

    public SearchAPIResult() 
    { 

    } 

    public SearchAPIResult(string status) 
    { 
     Status = status; 
    } 
} 

SearchAPIResult是:

public sealed class SearchResults 
{ 
    public string TextAnswer; 

    public List<APIResultWidget> Items; 

    public SearchResults() 
    { 
     Items = new List<APIResultWidget>(); 
    } 
} 

,这里的对象APIResultWidget是具有约10类继承自它的抽象类。

问题是JSON对象没有自动的东西(比如JSON.NET中的typeNameHandling)来指导反序列化器将10个派生类中的哪个对象转换为。而不是对象打上了两个字段:类型和子类型......像下面

{ 
    "Status": "OK", 
    "Query": { 
     "Query": "this is a query", 
     "QueryLanguage": "EN" 
    }, 
    "Result": { 
     "TextAnswer": "This is your text answer", 
     "Items": [{ 
       "Type": "list", 
       "SubType": null, 
       "Title": null, 
       "Items": [] 
      }, { 
       "Type": "text", 
       "Content": "this is some content" 
      } 
     ] 
    } 
} 
在前面的JSON对象

,结果列表包含两个对象,一个:

{ 
    "Type": "list", 
    "SubType": null, 
    "Title": null, 
    "Items": [] 
} 

映射到类类型的listWidget(其从抽象APIResultWidget和两个继承:

{ 
    "Type": "text", 
    "Content": "this is some content" 
} 

映射到textWidget类也从相同的abstra继承CT类

当我使用Json.NET方式

SearchAPIResult converted = (SearchAPIResult)JsonConvert.DeserializeObject(json, typeof(SearchAPIResult), new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); 

它抛出以下异常

无法创建类型Kngine.API.APIResultWidget的一个实例。 Type是一个接口或抽象类,不能实例化。 Path'Result.Items [0] .Type',line 1,position 136.

我猜想有一种自定义的方式指出该类型是由字段Type和SubType定义的,并给出Converter那个自定义类型注释器,对吗?

回答

2

其实我implmented的解决方案是很基本的解决方案,我从扩展的的System.Web类JavaScriptConverter。扩展,我实现了deseriealize方法,它自动接收原始对象中的每个小json对象作为Dictionary,并且我可以根据对象本身填充字段,这是一种手动方式,但那是我唯一的解决方案能想出和作品,自定义类implmentation看起来是这样的:

class myCustomResolver : JavaScriptConverter 

{ 
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) 
    { 
     if (type == typeof(APIResultWidget)) 
     { 
      switch ((string)dictionary["Type"]) 
      { 
       case "weather": 
        { 
         WeatherWidget x = new WeatherWidget(); 
         x.Location = (string)dictionary["Location"]; 
         x.Current = (CurrentWeather)dictionary["Current"]; 
         //x.Forcast = (List<WeatherForcastItem>)dictionary["Forcast"]; 

         System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Forcast"]); 
         foreach (var item in itemss) 
         { 
          x.Forcast.Add(serializer.ConvertToType<WeatherForcastItem>(item)); 
         } 

         return x; 
        }; 
       case "text": 
        { 
         TextWidget x = new TextWidget(); 
         x.Content = (string)dictionary["Content"]; 
         return x; 
        }; 
       case "keyValueText": 
        { 
         KeyValueTextWidget x = new KeyValueTextWidget(); 
         x.Key = (string)dictionary["Key"]; 
         x.Key = (string)dictionary["Value"]; 
         x.Key = (string)dictionary["ValueURL"]; 
         return x; 
        }; 
       case "keyValuesText": 
        { 
         KeyValuesTextWidget x = new KeyValuesTextWidget(); 
         x.Key = (string)dictionary["Key"]; 
         //x.Values = (List<ValueItem>)dictionary["ValueItem"]; 

         System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["ValueItem"]); 
         foreach (var item in itemss) 
         { 
          x.Values.Add(serializer.ConvertToType<ValueItem>(item)); 
         } 


         return x; 


        }; 
       case "url": 
        { 
         URLWidget x = new URLWidget(); 
         x.ThumbnailImageURL = (string)dictionary["ThumbnailImageURL"]; 
         x.Title = (string)dictionary["Title"]; 
         x.URL = (string)dictionary["URL"]; 
         x.HTMLContent = (string)dictionary["HTMLContent"]; 
         return x; 

        }; 
       case "map": 
        { 
         MapWidget x = new MapWidget(); 
         System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Pins"]); 
         foreach (var item in itemss) 
         { 
          x.Pins.Add(serializer.ConvertToType<MapPoint>(item)); 
         } 

         //x.Pins = (List<MapPoint>)dictionary["Pins"]; 
         return x; 

        }; 
       case "image": 
        { 
         ImageWidget x = new ImageWidget(); 
         x.Title = (string)dictionary["Title"]; 
         x.ImageURL = (string)dictionary["ImageURL"]; 
         x.ThumbnailURL = (string)dictionary["ThumbnailURL"]; 
         x.PageURL = (string)dictionary["PageURL"]; 
         return x; 
        }; 
       case "html": 
        { 
         HTMLWidget x = new HTMLWidget(); 
         x.Title = (string)dictionary["Title"]; 
         x.HTML = (string)dictionary["HTML"]; 
         return x; 


        }; 
       case "entity": 
        { 
         EntityWidget x = new EntityWidget(); 
         x.SubType = (string)dictionary["SubType"]; 
         x.Title = (string)dictionary["Title"]; 
         x.Abstract = (string)dictionary["Abstract"]; 
         x.ImageURL = (string)dictionary["ImageURL"]; 
         x.Url = (string)dictionary["Url"]; 
         return x; 

        }; 
       case "chart": 
        { 
         ChartWidget x = new ChartWidget(); 
         x.Title = (string)dictionary["Title"]; 
         //x.Categories = (List<string>)dictionary["Categories"]; 
         System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Categories"]); 
         foreach (var item in itemss) 
         { 
          x.Categories.Add(serializer.ConvertToType<string>(item)); 
         } 



         System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["Data"]); 
         foreach (var item in itemss2) 
         { 
          x.Data.Add(serializer.ConvertToType<ChartsData>(item)); 
         } 

         //x.Data = (List<ChartsData>)dictionary["Data"]; 
         return x; 
        }; 
       case "businessEntity": 
        { 
         BusinessEntityWidget x = new BusinessEntityWidget(); 
         x.SubType = (string)dictionary["SubType"]; 
         x.Title = (string)dictionary["Title"]; 
         x.Abstract = (string)dictionary["Abstract"]; 
         x.ImageURL = (string)dictionary["ImageURL"]; 
         x.URL = (string)dictionary["URL"]; 
         //x.Attributes = (List<KeyValueTextWidget>)dictionary["Attributes"]; 
         System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Attributes"]); 
         foreach (var item in itemss) 
         { 
          x.Attributes.Add(serializer.ConvertToType<KeyValueTextWidget>(item)); 
         } 

         x.Address = (string)dictionary["Address"]; 
         x.Phone = (string)dictionary["Phone"]; 
         x.Lat = (double)dictionary["Lat"]; 
         x.Lng = (double)dictionary["Lng"]; 


         System.Collections.ArrayList itemss2 = ((System.Collections.ArrayList)dictionary["OtherURLs"]); 
         foreach (var item in itemss2) 
         { 
          x.OtherURLs.Add(serializer.ConvertToType<URLWidget>(item)); 
         } 
         //x.OtherURLs = (List<URLWidget>)dictionary["OtherURLs"]; 

         return x; 



        }; 

       case "list": 
        { 
         switch ((string)dictionary["SubType"]) 
         { 
          case null: 
           { 
            ListWidget x = new ListWidget(); 
            x.Title = (string)dictionary["Title"]; 
            System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); 
            foreach (var item in itemss) 
            { 
             x.Items.Add(serializer.ConvertToType<APIResultWidget>(item)); 
            } 
            return x; 

           }; 
          case "videos": 
           { 
            ListOfVideosWidget x = new ListOfVideosWidget(); 

            System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); 
            foreach (var item in itemss) 
            { 
             x.Items.Add(serializer.ConvertToType<URLWidget>(item)); 
            } 
            return x; 
           }; 
          case "images": 
           { 
            ListOfImagesWidget x = new ListOfImagesWidget(); 


            System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); 
            foreach (var item in itemss) 
            { 
             x.Items.Add(serializer.ConvertToType<ImageWidget>(item)); 
            } 
            return x; 
           }; 
          case "webResults": 
           { 

            ListOfWebsitesWidget x = new ListOfWebsitesWidget(); 

            System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); 
            foreach (var item in itemss) 
            { 
             x.Items.Add(serializer.ConvertToType<URLWidget>(item)); 
            } 
            return x; 
           }; 
          case "businesses": 
           { 
            ListOfBusinessesWidget x = new ListOfBusinessesWidget(); 
            x.Title = (string)dictionary["Title"]; 
            System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["Items"]); 
            foreach (var item in itemss) 
            { 
             x.Items.Add(serializer.ConvertToType<BusinessEntityWidget>(item)); 
            } 
            return x; 
           }; 



         } 
        }; break; 


      } 
     } 

     else //in case of objects not inheriting from the abstract class, in this case we identify each one by something else, not "type" 
     { 
      if (dictionary.ContainsKey("Day")) //WeatherForcastItem 
      { 
       WeatherForcastItem x = new WeatherForcastItem(); 
       x.Day = (string)dictionary["Day"]; 
       x.Hi = (string)dictionary["Hi"]; 
       x.Lo = (string)dictionary["Lo"]; 
       x.Status = (string)dictionary["Status"]; 
       x.IconURL = (string)dictionary["IconURL"]; 
       return x; 

      } 
      else if (dictionary.ContainsKey("Temprature")) // CurrentWeather 
      { 
       CurrentWeather x = new CurrentWeather(); 
       x.Temprature = (string)dictionary["Temprature"]; 
       x.Status = (string)dictionary["Status"]; 
       x.WindSpeed = (string)dictionary["WindSpeed"]; 
       x.WindDirection = (string)dictionary["WindDirection"]; 
       x.Humidity = (string)dictionary["Humidity"]; 
       x.IconURL = (string)dictionary["IconURL"]; 
       x.IsNight = (string)dictionary["IsNight"]; 
       return x; 

      } 
      else if (dictionary.ContainsKey("Lat")) //MapPoint 
      { 
       MapPoint x = new MapPoint(); 
       x.Title = (string)dictionary["Title"]; 
       x.Lat = (double)dictionary["Lat"]; 
       x.Lng = (double)dictionary["Lng"]; 
       return x; 
      } 
      else if (dictionary.ContainsKey("Value")) //ValueItem 
      { 
       ValueItem x = new ValueItem(); 
       x.Value = (string)dictionary["Value"]; 
       x.ValueURL = (string)dictionary["ValueURL"]; 
       return x; 
      } 
      else if (dictionary.ContainsKey("name")) //ChartsData 
      { 
       ChartsData x = new ChartsData(); 
       x.name = (string)dictionary["name"]; 
       System.Collections.ArrayList itemss = ((System.Collections.ArrayList)dictionary["name"]); 
       foreach (var item in itemss) 
       { 
        x.values.Add(serializer.ConvertToType<string>(item)); 
       } 
       return x; 
      } 
     } 
     return null; 
    } 

    public override IDictionary<string, object> Serialize(
object obj, 
JavaScriptSerializer serializer) 
    { return null; } 

    private static readonly Type[] _supportedTypes = new[] 
{ 
    typeof(APIResultWidget) 
}; 

    public override IEnumerable<Type> SupportedTypes 
    { 
     get { return _supportedTypes; } 
    } 

} 

应地图中的每个JSON对象,它是正确的班,deseriealizer的使用是相当容易的,那么:

var serializer = new JavaScriptSerializer(); 
serializer.RegisterConverters(new[] { new myCustomResolver() }); 
var dataObj = serializer.Deserialize<SearchAPIResult>(response); 

这解决了我在抽象类中遇到的反序列化问题,因为它完全解决了问题这些课程首先是相互关联的。我不知道这是否是最正确的解决方案,但至少它解决了我的问题

1

首先,这是一种更好的方式来反序列化对象:

var converted = JsonConvert.DeserializeObject<SearchAPIResult>(json); 

反正我觉得你的问题是,你正在试图教导串行只有一个APIResultWidget的列表反序列化的Item属性。你可以更新你的帖子,该类吗?我们可以更容易地帮助你。

+0

APIResultWidget只是一个抽象类,它实际上是空的 – SKandeel 2013-05-13 15:11:09

0

默认情况下,ServiceStack仅序列化公共属性。为了得到它的序列化的公共领域以及你需要用它配置:

JsConfig.IncludePublicFields = true; 
+0

它不是一个序列化问题,它是一个反序列化问题,我无法操作序列化过程。 – SKandeel 2013-05-13 20:43:14