2014-12-09 4 views
3

У меня есть класс с внутренним списком. Я не хочу, чтобы пользователь этого класса мог напрямую взаимодействовать с этим списком, так как я хочу, чтобы он сортировал и выполнял расчет (который зависит от порядка), прежде чем возвращать его.Как десериализовать только чтение Список с Json.Net

Выставляю

AddItem(Item x) 

и

IEnumerable<Item> Items 
{ 
    get { // returns a projection of internal list } 
} 

Сериализация работал нормально, но десериализации покинул список пуст. Я решил, что это потому, что у меня не было сеттера. Поэтому я добавил один, который позволил вам установить список, но только если внутренний список пуст. Но это не решило проблему, выяснилось, что NewtonSoft не вызывает setter, он только вызывает getter для получения списка, а затем добавляет к нему каждый элемент, который, поскольку мой getter возвращает проецируемый список, эти элементы добавляются к объекту, который сразу же удаляется после завершения десериализации.

Как сохранить доступ к моему списку только для чтения и в то же время разрешить несколько простое десериализацию?

+0

сделал вы пробовали 'список уаг = новый объект [] {....}; var ser = JsonConvert.SerializeObject (list, Formatting.Indented); '? – Kiquenet

+0

@Kiquenet: Я не десериализую список напрямую, я десериализую объект, который имеет список как свойство только для чтения. –

+0

@NeilN Что сработало для вас? – Prakhar

ответ

1

То, что работало для меня было следующее:

[JsonProperty(PropertyName = "TargetName")] 
private List<SomeClass> _SomeClassList { get; set; } 
public IReadOnlyList<SomeClass> SomeClassList 
{ 
    get 
    { 
     return this._SomeClassList.AsReadOnly(); 
    } 
} 

Затем сделать функцию, чтобы предотвратить SomeClassList сериализовать:

public bool ShouldSerializeSomeClassList() { return false; } 
1

Это +2017 сейчас, и я могу сообщить, что Json .NET 10.x, похоже, прав. Вот мой тест-случай:

void Main() 
{ 
    Poco p = new Poco(); 
    p.Items.Add("foo"); 
    p.Items.Add("bar"); 

    p.Dump(); // Linqpad command to show a visual objet dump in the output pane 

    String serialized = Newtonsoft.Json.JsonConvert.SerializeObject(p); 

    serialized.Dump(); 
    // serialized == "{"Items":["foo","bar"]}" 

    Poco p2 = (Poco)Newtonsoft.Json.JsonConvert.DeserializeObject(serialized, typeof(Poco)); 

    p2.Dump(); 
} 

class Poco 
{ 
    public List<String> Items { get; } = new List<String>(); 
} 

Когда я запускаю это, отвалы p и p2 идентичны.

+1

Poco не содержит список только для чтения. OP хотел список, который пользователь не мог изменить. С помощью этого кода я могу написать 'p2.Items.Add (" foobar ");' после deserialisation .. – Jamey

0

Я наткнулся на ответ на Stackoverflow в разделе комментариев, но он не был проголосован. И я дам вам более подробный ответ здесь:

public class State 
{ 
     [Newtonsoft.Json.JsonProperty] 
     public double Citizens { get; private set; } 

     [Newtonsoft.Json.JsonProperty] 
     public float Value { get { return pValue; } } 
     private float pValue = 450000.0f; 

     public List<string> BeachList { get; } = new List<string>(); 

    public State() 
    { 
    } 

    public State(double _Citizens) 
    { 
     this.Citizens = _Citizens; 
    } 
} 

... 

      State croatia = new State(30.0D); 
      croatia.BeachList.Add("Bol na Braču"); 
      croatia.BeachList.Add("Zrće"); 

      string croatiaSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(croatia); 

      State slovenia = Newtonsoft.Json.JsonConvert.DeserializeObject<State>(croatiaSerialized); 

Так, хорватия и словения теперь оба имеют одинаковые значения свойств. Я добавил «Граждане» и «Свойства», чтобы увидеть, хотите ли вы работать с тем или иным способом.

Благодаря Саиб Амини (Private setters in Json.Net)

+0

Моя проблема выходит за рамки просто быть в состоянии установить что-то через частный сеттер. Хотя ваше решение будет работать для примитивных типов, таких как float или double, оно не будет работать для IEnumerables, как в моем вопросе. Сверху: «NewtonSoft не вызывает setter, он только вызывает getter для получения списка, а затем добавляет к нему каждый элемент». –

+0

Вы правы в этом. Я дойду до этого. Я вернусь здесь @NeilN. – MaGnumX

1

С Newtonsoft вы можете использовать CustomCreationConverter<T> или абстрактную JsonConverter, вы должны реализовать метод Create и ReadJson.

Метод ReadJson - это то, где конвертер выполнит десериализацию по умолчанию, вызывающую базовый метод, оттуда каждый элемент внутри коллекции readonly можно десериализовать и добавить с помощью метода AddItem.

Любая пользовательская логика может быть реализована внутри AddItem.

Последний шаг настройки нового преобразователя для десериализации с атрибутом [JsonConverter(typeof(NavigationTreeJsonConverter))] или в JsonSerializerSettings

public class ItemsHolderJsonConverter : CustomCreationConverter<ItemsHolder> 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return typeof(ItemsHolder).IsAssignableFrom(objectType); 
    } 

    public override object ReadJson(JsonReader reader, 
            Type objectType, 
            object existingValue, 
            JsonSerializer serializer) 
    { 

     JObject jObject = JObject.Load(reader); 

     ItemsHolder holder = base.ReadJson(CreateReaderFromToken(reader,jObject), objectType, existingValue, serializer) as ItemsHolder; 

     var jItems = jObject[nameof(ItemsHolder.Items)] as JArray ?? new JArray(); 
     foreach (var jItem in jItems) 
     { 
      var childReader = CreateReaderFromToken(reader, jItem); 
      var item = serializer.Deserialize<Item>(childReader); 
      holder.AddItem(item); 
     } 

     return holder; 
    } 

    public override ItemsHolder Create(Type objectType) 
    { 
     return new ItemsHolder(); 
    } 

    public static JsonReader CreateReaderFromToken(JsonReader reader, JToken token) 
    { 
     JsonReader jObjectReader = token.CreateReader(); 
     jObjectReader.Culture = reader.Culture; 
     jObjectReader.DateFormatString = reader.DateFormatString; 
     jObjectReader.DateParseHandling = reader.DateParseHandling; 
     jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling; 
     jObjectReader.FloatParseHandling = reader.FloatParseHandling; 
     jObjectReader.MaxDepth = reader.MaxDepth; 
     jObjectReader.SupportMultipleContent = reader.SupportMultipleContent; 
     return jObjectReader; 
    } 
} 
Смежные вопросы