2016-09-25 3 views
1

Я использую вариацию шаблона объекта типа (в основном, умные перечисления). Поскольку эту проблему лучше всего объяснить с помощью кода, я буду прыгать прямо в нее.JSON.Net сохранить ссылку на статический объект

class Program 
    { 
     static void Main(string[] args) 
     { 
      Test C = Test.B; 
      Console.WriteLine(C == Test.B); //Returns true 

      string Json = JsonConvert.SerializeObject(C); 

      C = JsonConvert.DeserializeObject<Test>(Json); 
      Console.WriteLine(C == Test.B); //Returns false 
     } 
    } 

    public class Test 
    { 
     public int A { get; set; } 

     public Test(int A) 
     { 
      this.A = A; 
     } 

     public static Test B = new Test(100); 
    } 

В этом примере тесте является объектом типа, и экземпляры него назначены это статическое поле, B. В сценариях реальной жизни было бы кратен эти статических полей, каждый из которых представляет другой тип. Когда я сериализую и десериализую, тестовый объект сериализуется исключительно как данные. Я понимаю, почему это происходит, но я не знаю, что с этим делать. Я хотел бы как-то сохранить экземпляры Test как ссылки на статический член в этом классе.

ответ

0

Невозможно по умолчанию, поскольку десериализатор JSON не заботится о существующих ссылках или статических объектах в вашем классе.

Вы можете сравнить это с помощью настраиваемого метода Equals, но я думаю, что это не то, что вы хотите.

0

Не сериализуйте MyObj.Test, подавите это с помощью атрибута Ignore. Вместо этого выведите свойство MyObj.TestID, которое возвращает MyObj.Test.ID. Когда TestID установлен в MyObj, загрузите Test из статической коллекции, введенной с помощью идентификатора, и установите для MyObj.Test значение.

0

Прежде всего, шаблоны Type Object предполагается использовать, когда вы не хотите проходить иерархию наследования каждый раз, когда вы определяете новую производную базового класса. Наличие объекта типа, прикрепленного как static, не заставил себя чувствовать себя на первом месте, чтобы быть честным. Как вы уже упомянули, это вариант, который я не собираюсь вникать в это.

Похоже, что вы хотите сохранить ссылку даже после десериализации с помощью json.net.

Теперь, если вы хотите сделать это, вы можете посмотреть here.

Взятие фрагментов из вышеупомянутой ссылки, так как лучше иметь образец здесь, так как это ответ StackOverflow. Он должен поддерживать даже мертвую связь.

Ваш первый вариант - использовать значение по умолчанию PreserveReferencesHandling. Соответствующий образец следует за тем, где вы можете ссылаться на одни и те же объекты в списке и указывать на него. Я не думаю, что это на самом деле держит старую ссылку, но уверен, что помогает, когда у вас есть те же самые вещи в списке, и вы не хотите идти со своими собственными IEqualityComparer или IEquatable реализаций:

string json = JsonConvert.SerializeObject(people, Formatting.Indented, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); 

//[ 
// { 
// "$id": "1", 
// "Name": "James", 
// "BirthDate": "1983-03-08T00:00Z", 
// "LastModified": "2012-03-21T05:40Z" 
// }, 
// { 
// "$ref": "1" 
// } 
//] 

List<Person> deserializedPeople = JsonConvert.DeserializeObject<List<Person>>(json, 
    new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); 

Console.WriteLine(deserializedPeople.Count); 
// 2 

Person p1 = deserializedPeople[0]; 
Person p2 = deserializedPeople[1]; 

Console.WriteLine(p1.Name); 
// James 
Console.WriteLine(p2.Name); 
// James 

bool equal = Object.ReferenceEquals(p1, p2); 
// true 

Вы можете использовать IsReference атрибут для управления свойства которого будет храниться в качестве ссылок:

[JsonObject(IsReference = true)] 
public class EmployeeReference 
{ 
    public string Name { get; set; } 
    public EmployeeReference Manager { get; set; } 
} 

Теперь, если вы хотите сохранить ту же ссылку в коде для себя (я не думаю, что это действительно хороший дизайн в любом случае, вы, возможно, просто нужен Equality метод сравнения и сделать с ним), вам нужен пользовательский IReferenceResolver определено here.

Кроме того, если вы хотите иметь что-то подобное, посмотрите не дальше, чем исходный код Json.net here.

Это IdReferenceResolver, который вы можете использовать, чтобы сохранить ссылку на объект как Guid и, возможно, использовать его по-своему.

И если вы хотите знать, как работают DefaultReferenceResolver, вы можете взглянуть на этот stackoverflow thread.

0

Что вы ищете является поддержкой интерфейса IObjectReference:

реализует этот интерфейс на объектах, которые являются ссылками на другой объект, который не может быть разрешен, пока текущий объект не будет полностью восстановлен. Во время этапа фиксации любой объект, реализующий IObjectReference, запрашивается для своего реального объекта и этот объект вставляется в граф.

К сожалению, Json.NET не поддерживает этот интерфейс. Тем не менее, оказалось довольно легко расширить Json.NET для поддержки этого интерфейса в тех случаях, когда данный тип также реализует ISerializable. Это вполне разумное ограничение, учитывая, что на практике эти два интерфейса часто используются вместе, как показано в documentation example.

Во-первых, ввести следующее custom contract resolver:

public class ISerializableRealObjectContractResolver : DefaultContractResolver 
{ 
    // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. 
    // http://www.newtonsoft.com/json/help/html/ContractResolver.htm 
    // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm 
    // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." 
    static ISerializableRealObjectContractResolver instance; 

    static ISerializableRealObjectContractResolver() { instance = new ISerializableRealObjectContractResolver(); } 

    public static ISerializableRealObjectContractResolver Instance { get { return instance; } } 

    public ISerializableRealObjectContractResolver() 
     : base() 
    { 
     this.IgnoreSerializableInterface = false; 
    } 

    protected override JsonISerializableContract CreateISerializableContract(Type objectType) 
    { 
     var contract = base.CreateISerializableContract(objectType); 

     var constructor = contract.ISerializableCreator; 
     contract.ISerializableCreator = args => 
     { 
      var obj = constructor(args); 
      if (obj is IObjectReference) 
      { 
       var context = (StreamingContext)args[1]; 
       obj = ((IObjectReference)obj).GetRealObject(context); 
      } 
      return obj; 
     }; 
     return contract; 
    } 
} 

Теперь измените ваш псевдопользователей-Enum Test типа для реализации ISerializable и IObjectReference:

public class Test : ISerializable, IObjectReference 
{ 
    readonly int a; 

    public int A { get { return a; } } 

    public Test(int A) 
    { 
     this.a = A; 
    } 

    public static readonly Test B = new Test(100); 

    #region ISerializable Members 

    protected Test(SerializationInfo info, StreamingContext context) 
    { 
     a = info.GetInt32("A"); 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("A", A); 
    } 

    #endregion 

    #region IObjectReference Members 

    public object GetRealObject(StreamingContext context) 
    { 
     // Check all static properties to see whether the key value "A" matches. If so, return the static instance. 
     if (this.A == B.A) 
      return B; 
     return this; 
    } 

    #endregion 
} 

Я также сделал тип неизменны, так что, очевидно, требование здесь.

Теперь ваше устройство тест будет проходить при использовании этого контракта распознаватель:

Test C = Test.B; 
Console.WriteLine(C == Test.B); //Returns true 

string Json = JsonConvert.SerializeObject(C, new JsonSerializerSettings { ContractResolver = ISerializableRealObjectContractResolver.Instance }); 
Console.WriteLine(Json); 

C = JsonConvert.DeserializeObject<Test>(Json, new JsonSerializerSettings { ContractResolver = ISerializableRealObjectContractResolver.Instance }); 
Console.WriteLine(C == Test.B); //Still returns true  

if (!object.ReferenceEquals(C, Test.B)) 
{ 
    throw new InvalidOperationException("!object.ReferenceEquals(C, Test.B)"); 
} 
else 
{ 
    Console.WriteLine("Global singleton instance deserialized successfully."); 
} 

Однако обратите внимание, что Json.NET поддерживает только интерфейс ISerializable в полном доверии.

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