Я обмениваюсь данными между приложением. NET Core 3.1 (+. NET Standard 2.1) и приложением. NET Framework 4.6, которое я полностью контролирую. . NET Core запускает процесс. NET Framework, и эти два процесса взаимодействуют друг с другом через перенаправленный stdout / stdin.
My. NET Приложение Core (Core) хочет сообщить моему . NET Приложение Framework (FW) для запуска метода с данными.
Используемый им API похож на SendMessage("FW_MethodName", new List<object> { param1, param2 });
(Эта операция должна происходить в процессе FW, а FW не знает, делайте это до тех пор, пока Core не скажет это сделать).
Я использую Json. NET для сериализации и сериализации вышеприведенного сообщения примерно так:
public class ATypeOfMessage : BaseMessage
{
public string Name { get; set; }
public List<object> Args { get; set; }
public Message(string name, List<object> args)
{
Name = name;
Args = args;
}
}
ATypeOfMessage message = new ATypeOfMessage("FW_MethodName", new List<object> { param1, param2 });
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandline = TypeNameHandling.All };
string serializedMessage = JsonConvert.SerializeObject(message, typeof(BaseMessage), settings);
// the idea being that I have multiple types of messages, so I serialize/deserialize BaseMessages and
// save off $type metadata, so I can deserialize the string into the correct derived class (ATypeOfMessage)
myFWProcess.StandardInput.WriteLine(serializedMessage);
FW затем читает сообщение :
string serializedMessage = Console.In.ReadLine();
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandline = TypeNameHandling.All };
BaseMessage message = JsonConvert.DeserializeObject<BaseMessage>(serializedMessage, settings);
switch (message)
{
case ATypeOfMessage myMessage:
// ATypeOfMessage is doubly-defined in FW and Core
// get the methodInfo for method myMessage.Name and do...
object answer = methodInfo.Invoke(this, myMessage.Args.ToArray());
break;
}
Это прекрасно работает для простых типов (например, int, string), но в тот момент, когда я передаю что-то более сложное, например byte[]
, происходит десериализация.
Это происходит потому, что Поле $type
, которое Json. NET сохраняет, содержит сборку System.Private.CoreLib
, к которой нет доступа из FW.
Я попытался обойти эту проблему, перенаправив имя какой-либо сборки с помощью Json. NET s SerializationBinder
:
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandline = TypeNameHandling.All, SerializationBinder = new MySerializationBinder() };
class MySerializationBinder : DefaultSerializationBinder
{
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
base.BindToName(serializedType, out assemblyName, out typeName);
if (assemblyName.StartsWith("System.Private.CoreLib"))
{
assemblyName = "mscorlib";
}
}
}
Это немного глупо, и я ' Я предпочел бы не делать этого, но он отлично работает для более сложных типов, поэтому я могу сделать SendMessage("FW_MethodName", new List<object> { byteArray, stringArray });
Однако, даже более сложные типы, такие как Dictionary<string, List<string>>
break. $type
Данные, которые были сохранены:
\"$type\":\"System.Collections.Generic.Dictionary`2[[System.String, System.Private.CoreLib],[System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib]], mscorlib\",\"k1\":[\"a\",\"b\",\"c\"],\"k2\":[\"a\",\"b\",\"c\"],\"k3\":[\"a\",\"b\",\"c\"]
, который снова содержит CoreLib, поэтому FW не может десериализовать его.
Еще одна вещь, которую я попытался, - это изменить ATypeOfMessage
принять список строк вместо объектов, а затем я делаю двойную сериализацию.
Сериализация ядра:
public SendMessage(string methodName, List<object> args)
{
List<string> serializedArgs = new List<string>();
foreach (object arg in args)
{
serializedArgs.Add(JsonConvert.SerializeObject(arg);
}
// ... same logic as before to create ATypeOfMessage, serialize the whole thing, and send it to FW
}
Десериализация FW:
// ... deserializes the object as before, but before invoking the method, we deserialize the specific args
MethodInfo methodInfo = typeof(ClassWithMethods).GetMethod(myMessage.Name);
ParameterInfo[] paramInfo = methodInfo.GetParameters();
object[] finalParams = new object[myMessage.Args.Count];
for (int i = 0 ; i < myMessage.Args.Count; i++)
{
object arg = myMessage.Args[i];
finalParameters[i] = JsonConvert.DeserializeObject(arg, paramInfo[i].ParameterType);
}
object answer = methodInfo.Invoke(this, finalParameters);
Кажется, это работает нормально, но я бы предпочел метод, который не предусматривает двойной сериализации каждого аргумента в строку. Есть ли такой метод?
Последнее, что я пытался, это не сохранять $type
информацию для моих аргументов:
[JsonProperty(ItemTypeNameHandling = TypeNameHandling.None)]
public List<object> Args { get; set; }
Затем при десериализации аргументы становятся JObjects, JArrays, или JValues (все типы JTokens) согласно разделу «Нетипизированные объекты» здесь .
А затем, когда я десериализуюсь и готовлюсь к вызову метода, я конвертирую свои аргументы следующим образом:
for (int i = 0 ; i < myMessage.Args.Count; i++)
{
object arg = msg.Args[i];
if (arg is JToken)
{
arg = (arg as JToken).ToObject(paramInfo[i].ParameterType);
}
finalParameters[i] = arg;
}
Теперь у меня противоположная проблема, как и раньше. Сложные типы (string[]
, Dictionary<string, List<string>>
) десериализуются правильно, но тип, подобный byte[]
, не делает этого, потому что Json. NET превращает массив байтов в строку в кодировке base64, и поэтому, когда FW получает это, он не знает, что строка действительно должна быть байтовым массивом.
Как я могу аккуратно отправлять такие данные между Core и FW? Может быть, мне нужно использовать JsonConverters
для чего-то подобного?