У меня есть собственный тип, производный от типа DynamicObject
. Этот тип имеет фиксированные свойства, объявленные в типе. Таким образом, он позволяет пользователю предоставлять некоторые требуемые свойства в дополнение к любым динамическим свойствам, которые они хотят. Когда я использую метод JsonConvert.DeserializeObject<MyType>(json)
для десериализации данных для этого типа, он не устанавливает объявленные свойства, но эти свойства доступны через свойство индексатора объекта динамического объекта. Это говорит мне, что он просто рассматривает объект как словарь и не пытается вызвать объявленные установщики свойств и не использует их для вывода информации о типе свойства.
Кто-нибудь сталкивался с такой ситуацией раньше? Есть идеи, как я могу указать классу JsonConvert
учитывать объявленные свойства при десериализации данных объекта?
Я пытался использовать собственный JsonConverter
, но для этого мне нужно было написать сложные методы чтения и записи JSON. Я надеялся найти способ ввести информацию о контракте на собственность, переопределив JsonContractResolver
или JsonConverter
и т. Д.
//#define IMPLEMENT_IDICTIONARY
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using Newtonsoft.Json;
namespace ConsoleApp1
{
class Program
{
public class MyDynamicObject : DynamicObject
#if IMPLEMENT_IDICTIONARY
, IDictionary<string, object>
#endif
{
private Dictionary<string, object> m_Members;
public MyDynamicObject()
{
this.m_Members = new Dictionary<string, object>();
}
#if IMPLEMENT_IDICTIONARY
public int Count { get { return this.m_Members.Count; } }
public ICollection<string> Keys => this.m_Members.Keys;
public ICollection<object> Values => this.m_Members.Values;
bool ICollection<KeyValuePair<string, object>>.IsReadOnly => false;
/// <summary>
/// Gets or sets the specified member value.
/// </summary>
/// <param name="memberName">Name of the member in question.</param>
/// <returns>A value for the specified member.</returns>
public object this[string memberName]
{
get
{
object value;
if (this.m_Members.TryGetValue(memberName, out value))
return value;
else
return null;
}
set => this.m_Members[memberName] = value;
}
#endif
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
this.m_Members.TryGetValue(binder.Name, out result);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.m_Members[binder.Name] = value;
return true;
}
public override bool TryDeleteMember(DeleteMemberBinder binder)
{
return this.m_Members.Remove(binder.Name);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
var names = base.GetDynamicMemberNames();
return this.m_Members.Keys;
}
#if IMPLEMENT_IDICTIONARY
bool IDictionary<string, object>.ContainsKey(string memberName)
{
return this.m_Members.ContainsKey(memberName);
}
public void Add(string memberName, object value)
{
this.m_Members.Add(memberName, value);
}
public bool Remove(string memberName)
{
return this.m_Members.Remove(memberName);
}
public bool TryGetValue(string memberName, out object value)
{
return this.m_Members.TryGetValue(memberName, out value);
}
public void Clear()
{
this.m_Members.Clear();
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> member)
{
((IDictionary<string, object>)this.m_Members).Add(member);
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> member)
{
return ((IDictionary<string, object>)this.m_Members).Contains(member);
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
((IDictionary<string, object>)this.m_Members).CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> member)
{
return ((IDictionary<string, object>)this.m_Members).Remove(member);
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return this.m_Members.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.m_Members.GetEnumerator();
}
#endif
}
public class ProxyInfo
{
public string Server;
public int Port;
}
public class CustomDynamicObject : MyDynamicObject
{
//[JsonProperty] // NOTE: Cannot do this.
public string Name { get; set; }
//[JsonProperty] // NOTE: Cannot do this.
public ProxyInfo Proxy { get; set; }
}
static void Main(string[] args)
{
dynamic obj = new CustomDynamicObject()
{
Name = "Test1",
Proxy = new ProxyInfo() { Server = "http://test.com/", Port = 10102 }
};
obj.Prop1 = "P1";
obj.Prop2 = 320;
string json = JsonConvert.SerializeObject(obj); // Returns: { "Prop1":"P1", "Prop2":320 }
// ISSUE #1: It did not serialize the declared properties. Only the dynamically added properties are serialized.
// Following JSON was expected. It produces correct JSON if I mark the declared properties with
// JsonProperty attribute, which I cannot do in all cases.
string expectedJson = "{ \"Prop1\":\"P1\", \"Prop2\":320, \"Name\":\"Test1\", \"Proxy\":{ \"Server\":\"http://test.com/\", \"Port\":10102 } }";
CustomDynamicObject deserializedObj = JsonConvert.DeserializeObject<CustomDynamicObject>(expectedJson);
// ISSUE #2: Deserialization worked in this case, but does not work once I re-introduce the IDictionary interface on my base class.
// In that case, it does not populate the declared properties, but simply added all 4 properties to the underlying dictionary.
// Neither does it infer the ProxyInfo type when deserializing the Proxy property value and simply bound the JObject token to
// the dynamic object.
}
}
}
Я ожидал, что он будет использовать отражение для определения свойства и информации о его типе, как это делается для обычных типов. Но кажется, что он просто рассматривает объект как обычный словарь.
Обратите внимание, что:
Я не могу удалить интерфейс
IDictionary<string, object>
, поскольку некоторые варианты использования моего API полагаются на то, что объект является словарем, а не динамическим.Добавление
[JsonProperty]
ко всем объявленным свойствам для сериализации нецелесообразно, поскольку его производные типы создаются другими разработчиками, и им не нужно явно заботиться о механизме сохранения.
Любые предложения о том, как я могу заставить его работать правильно?
MyDynamicObject
? Это должно работать, покаMyDynamicObject
реализовано правильно. См., Например, Сериализовать экземпляр класса, производного от класса DynamicObject, где проблема заключалась в невозможности переопределитьGetDynamicMemberNames()
. Возможно, вам потребуется пометить сериализуемые свойства с помощью[JsonProperty]
, см. C # Как сериализовать (JSON, XML) обычные свойства в классе, который наследуется от DynamicObject. - person dbc   schedule 02.04.2019GetDynamicMemberNames()
и 2) он реализовал интерфейсIDictionary<string, object>
. Когда я удалил интерфейсIDictionary
и реализовал методGetDynamicMemberNames
, сериализация сработала. - person B Singh   schedule 02.04.2019[JsonProperty]
, что я не могу сделать во всех случаях, поскольку эти производные типы создаются другими разработчиками, и им не нужно явно заботиться о механизме сохраняемости. Я могу попросить их изменить свои библиотеки, чтобы включить атрибут, но я хотел бы посмотреть, можно ли это обработать автоматически в коде моей базовой библиотеки. Есть предложения по этому поводу? - person B Singh   schedule 02.04.2019