Получение типа разрешения ошибки, указанного в JSON - PullRequest
0 голосов
/ 01 апреля 2020

Я использую newtownsoft JSON и получаю следующую ошибку:

Ошибка разрешения типа, указанного в JSON CharacterData, WorldServer. Путь 'data.1. $ Type', строка 1, позиция 67.

При десериализации следующим образом:

  JsonData receivedData = JsonConvert.DeserializeObject<JsonData>(sdata, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Objects,
        TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
    });

Часть Json, которая вызывает эту ошибку:

{"header":"0x004","data":{"1":{"$type":"CharacterData, WorldServer","characterId":1,"connectionId":0,"accountId":3,"name":"Riorage",

Вот как я ее сериализую:

string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto
});

Как я могу исправить эту ошибку? Было бы здорово, если бы при десериализации это не волновало , WorldServer. Важным аспектом является CharacterData.

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

1 Ответ

1 голос
/ 01 апреля 2020

Вы можете использовать Настраиваемый ISerializationBinder , чтобы указать настраиваемое значение $ type во время сериализации, а также для обработки того же настраиваемого значения $ type во время десериализации.

То есть вместо из:

"$type":"CharacterData, WorldServer"

Мы можем сериализовать в:

"$type":"CharacterData, Global"

И можем преобразовать это на приемном конце в известный CharacterData тип.

  • Обратите внимание, что при использовании TypeNameHandling = TypeNameHandling.Auto наше связующее будет вызываться только в том случае, если тип сериализуемого объекта не совпадает с его объявленным типом. Я думаю, что это уже для вас, поэтому не должно быть никаких проблем в этом отношении.

  • Пожалуйста, позвольте мне напомнить вам, что эта реализация должна быть закодирована с обеих сторон.

Чтобы убедиться в соответствии концепции вашему случаю; Я импровизировал следующую иерархию, где Transmission - это передаваемый объект.

public class Transmission
{
    public TransmissionData Data { get; set; }
}

public class TransmissionData
{

}
public class CharacterData : TransmissionData
{
    public string CharacterId { get; set; }
}

Вот пример преобразователя:

public class CharacterDataSerializationBinder : ISerializationBinder
{
    DefaultSerializationBinder defaultBinder = new DefaultSerializationBinder();

    void ISerializationBinder.BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        if (serializedType == typeof(CharacterData))
        {
            assemblyName = "Global";
            typeName = serializedType.Name;
        }
        else
        {
            defaultBinder.BindToName(serializedType, out assemblyName, out typeName);
        }
    }

    Type ISerializationBinder.BindToType(string assemblyName, string typeName)
    {
        if (typeName == typeof(CharacterData).Name && assemblyName == "Global")
        {
            return typeof(CharacterData);
        }
        else
        {
            return defaultBinder.BindToType(assemblyName, typeName);
        }
    }
}

И вот как его использовать:

    Transmission SendData = new Transmission()
    {
        Data = new CharacterData() { CharacterId = "454" }
    };

    string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        SerializationBinder = new CharacterDataSerializationBinder()
    });

    Transmission deserializedJson = JsonConvert.DeserializeObject<Transmission>(JSonData, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        SerializationBinder = new CharacterDataSerializationBinder()
    });

А вот вывод сериализации:

{"Data":{"$type":"CharacterData, Global","CharacterId":"454"}}

Надеюсь, это поможет.

ОБНОВЛЕНИЕ 1: ОП спросил, могут ли они использовать System.Runtime.Serialization.SerializationBinder вместо Json. Net s ISerializationBinder

Ответ - да. Json. Net поддерживает этот тип связующего вместе со своим собственным.

Вот пример реализации этого вида связующего:

public class CharacterDataCoreBinder : SerializationBinder
{
    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        typeName = serializedType.FullName;

        if (serializedType == typeof(CharacterData))
        {
            assemblyName = "Global";
        }
        else
        {
            assemblyName = serializedType.Assembly.GetName().Name;
        }
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        if (typeName == typeof(CharacterData).FullName && assemblyName == "Global")
        {
            return typeof(CharacterData);
        }
        else
        {
            return Type.GetType(typeName + ", " + assemblyName);
        }
    }
}

А вот как использовать это при сериализации / десериализации с Json. Net:

    string JSonData = JsonConvert.SerializeObject(SendData, Formatting.None, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        // SerializationBinder = new CharacterDataSerializationBinder()
        Binder = new CharacterDataCoreBinder()
    });

    Transmission deserializedJson = JsonConvert.DeserializeObject<Transmission>(JSonData, new JsonSerializerSettings
    {
        TypeNameHandling = TypeNameHandling.Auto,
        // SerializationBinder = new CharacterDataSerializationBinder()
        Binder = new CharacterDataCoreBinder()
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...