2014-09-05 3 views
0

У меня есть строка, содержащая список фруктов. Моя программа C# не может напрямую использовать Fruit, она должна отдать их Банану или Apple. Информация о том, что их использовать, включена в атрибут constructor в json.Относится к определенному типу в зависимости от атрибута JSON

Примечание: Я использую JSON.NET.

Это то, что я хочу добиться:

class Fruit{ public string constructor;} 
    class Banana : Fruit { public int monkey;} 
    class Apple : Fruit { public string shape;} 

    string json = @"[ 
      { 
       ""constructor"":""banana"", 
       ""monkey"":1 
      }, 
      { 
       ""constructor"":""apple"", 
       ""shape"":""round"" 
      }]"; 
     List<Fruit> fruitList = JsonConvert.DeserializeObject<List<Fruit>>(json); 
     List<Banana> bananaList = new List<Banana>(); 
     List<Apple> appleList = new List<Apple>(); 
     foreach(var fruit in fruitList){ 
      if(fruit.constructor == "banana") bananaList.Add((Banana)fruit); //bug 
      if(fruit.constructor == "apple") appleList.Add((Apple)fruit); //bug 
     } 

     //use bananaList and appleList for stuff 

Однако, я не могу сделать бросок. Есть ли какой-либо способ, чтобы bananaList содержал все объекты в json, которые имеют атрибуты конструктора, заданные как "banana", и то же самое для appleList?


РЕШЕНИЕ:

public static string objToJson(object obj) { 
     return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings { 
      TypeNameHandling = TypeNameHandling.Auto 
     }); 
    } 
    public static T jsonToObj<T>(string str) { 
     return JsonConvert.DeserializeObject<T>(str, new JsonSerializerSettings { 
      TypeNameHandling = TypeNameHandling.Auto 
     }); 
    } 
+1

созданных объектов являются фрукты. Класс-cast работает на * актуальном * объекте, поэтому Fruit не может быть «изменен» на Banana/Apple - он действителен только в том направлении. При этом, вероятно, это можно обработать с помощью CustomConverter (у Json.NET есть поддержка для восстановления типов через '$ type'), где такой конвертер будет соответствующим образом анализировать каждый объект в * реальный * экземпляр Banana/Apple. – user2864740

+0

Ого, и посмотрите здесь: http://stackoverflow.com/questions/8030538/how-to-implement-custom-jsonconverter-in-json-net-to-deserialize-a-list-of-base (в первом несколько ударов после поиска «пользовательского конвертера», на самом деле) – user2864740

+0

отстой, вы использовали мой ответ, но приняли другой ответ? –

ответ

1

В Json.NET, есть $type, которые вы можете использовать, чтобы указать, какой тип для десериализации в:

string json = @"[ 
      { 
       ""$type"":""Assembly.Namespace.banana"", 
       ""monkey"":1 
      }, 
      { 
       ""$type"":""Assembly.Namespace.apple"", 
       ""shape"":""round"" 
      }]"; 
+1

http://james.newtonking.com/json/help/index.html?topic=html/SerializeTypeNameHandling.htm – RainingChain

+0

Спасибо за ссылку! – Mrchief

0

JSON.NET только будет в состоянии разобрать, что он ожидает, чтобы разобрать. Невозможно предположить, какой полиморфный ребенок класса он хочет использовать.

Если у вас есть контроль над исходным JSON, вы можете сказать, что сериализатор должен включать данные о том, какой тип используется.

Если вы этого не сделаете, я считаю, что вам нужно будет прочитать данные вручную (или через JToken s или аналогичные), чтобы найти свойства constructor, прежде чем сообщать ему, что нужно разбирать правильные типы.

1

Вы пытаетесь сделать полиморфные десериализации, но вы просите десериализатор десериализации в Списке Фрукты. Вы не можете взлететь в Apple/Banana, потому что экземпляры будут иметь тип Fruit.

Если у вас есть контроль над json, вы можете сохранить json с информацией о типе в нем, и десериализатор автоматически создаст правильные типы.

public class Fruit{ } 
public class Banana : Fruit { public int monkey;} 
public class Apple : Fruit { public string shape;} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var list = new List<Fruit>(new Fruit[] {new Banana(), new Apple()}); 

     var serializerSettings = new JsonSerializerSettings{ TypeNameHandling = TypeNameHandling.Auto }; 

     var json = JsonConvert.SerializeObject(list, serializerSettings); 

     List<Fruit> fruitList = JsonConvert.DeserializeObject<List<Fruit>>(json, serializerSettings); 
     List<Banana> bananaList = new List<Banana>(); 
     List<Apple> appleList = new List<Apple>(); 
     foreach (var fruit in fruitList) 
     { 
      if (fruit is Banana) bananaList.Add((Banana) fruit); 
      if (fruit is Apple) appleList.Add((Apple) fruit); 
     } 
    } 
} 

Вы также можете использовать некоторые LINQ, чтобы найти яблоки/бананы легче, чем Еогеасп:

List<Banana> bananaList = fruitList.Where(f => f is Banana).Cast<Banana>().ToList(); 
List<Apple> appleList = fruitList.Where(f => f is Apple).Cast<Apple>().ToList(); 

Или вы можете использовать преобразователи, как описано здесь Deserializing polymorphic json classes without type information using json.net

+0

Что вы подразумеваете под «вы можете сохранить json с информацией о типе в нем»? И где я использую строку json точно? – RainingChain

+0

@RainingChain: TypeNameHandling.Свойство Auto/Object будет выписывать информацию типа $ во время сериализации, которую десериализатор может использовать для десериализации для соответствующего типа. – Mrchief

+0

отладить код и посмотреть на json, созданный после JsonConvert.SerializeObject. Вот увидишь –

Смежные вопросы