Я хотел бы иметь возможность контролировать, как JSON. NET генерирует свои мета-ссылочные идентификаторы, такие как "$id": "1"
. Возьмите следующий код:
public class Person
{
public string Name { get; set; }
public Person Mother { get; set; }
}
.
var settings = new Newtonsoft.Json.JsonSerializerSettings
{
PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented
};
Newtonsoft.Json.JsonConvert.DefaultSettings = () => settings;
var person = new Person
{
Name = "bob",
Mother = new Person { Name = "jane" }
};
var personJson = JsonConvert.SerializeObject(person);
var motherJson = JsonConvert.SerializeObject(person.Mother);
JSON для person
выглядит так:
{
"$id": "1",
"Name": "bob",
"Mother": {
"$id": "2",
"Name": "jane",
"Mother": null
}
}
Однако, если я сериализую person.Mother
напрямую, JSON выглядит следующим образом:
{
"$id": "1",
"Name": "jane",
"Mother": null
}
В первом JSON Джейн - "$id": "2"
, но сериализация Джейн напрямую - "$id": "1"
. Это поведение, которое я ожидал бы при нормальных условиях, так как сериализатор назначает идентификаторы в том порядке, в котором он пересекает объекты, но мне бы очень хотелось переопределить генерацию идентификаторов, чтобы я мог сделать его равным sh объекта ссылка сама. Таким образом, Джейн будет генерировать один и тот же идентификатор для каждого запущенного экземпляра программы каждый раз, независимо от того, сериализован ли он как член родительского или индивидуализирован.
ОБНОВЛЕНИЕ
В Пример кода в выбранном ответе и рекомендация в комментарии, я использовал IReferenceResolver
. Оказывается, я не могу его использовать, но я все равно добавлю код ниже. Причина, по которой это не сработает, заключается в том, что я пытаюсь убить JSON. NET как инструмент быстрого и грязного клонирования, поэтому я не могу винить его за то, что он не соответствует моим потребностям. С тех пор я вернулся к своей собственной утилите клонирования, поэтому она мне больше не нужна.
public class ObjectReferenceResolver : Newtonsoft.Json.Serialization.IReferenceResolver
{
readonly Dictionary<object, int> objectDic = new Dictionary<object, int>();
int maxId = 0;
//Called during serialization
public string GetReference(object context, object value)
{
//This method will return the meta $id that you choose. In this example, I am storing
//object references in a dictionary with an incremented ID. If the reference exists, I
//return its ID. Otherwise, I increment the ID and add the reference to the dictionary.
var id = 0;
if (objectDic.ContainsKey(value))
{
id = objectDic[value];
}
else
{
objectDic[value] = maxId++;
}
return id.ToString();
}
//Called during serialization
public bool IsReferenced(object context, object value)
{
//Return whether or not value exists in a reference bank.
//If false, the JSON will return as a full JSON object with "$id": "x"
//If true, the JSON will return "$ref": "x"
return objectDic.ContainsKey(value);
}
//Called during deserialization
public void AddReference(object context, string reference, object value)
{
//This method is called after the deserializer has created a new instance of the
//object. At this time, it's only the initial instance and no properties have been set.
//This method presents a problem because it does not allow you to create the instance or
//retrieve it from a repo and then return it for later use by the reference resolver.
//Therefore, I have to find the existing object by $id, remove it, and then add the new
//object created by the deseralizer. This creates the possibility for two instances of
//the same data object to exist within the running application, so, unfortunately, this
//will not work.
var e = objectDic.First(x => x.Value.ToString() == reference).Key;
objectDic.Remove(e);
objectDic[value] = reference.ParseInt().Value;
}
//Called during deserialization
public object ResolveReference(object context, string reference)
{
//This method retrieves an existing reference by $id and returns it.
var value = objectDic.FirstOrDefault(x => x.Value.ToString() == reference).Key;
return value;
}
}