将TypeNameAssemblyFormat与PCLs一起使用时Newtonsoft.Json的MissingMethodException
在PCLs中使用TypeNameAssemblyFormat是否有问题?使用Newtonsoft.Json的任何其他设置都没有问题,除非使用此序列化设置 以下是我的Json相关代码:将TypeNameAssemblyFormat与PCLs一起使用时Newtonsoft.Json的MissingMethodException,json,serialization,json.net,portable-class-library,missingmethodexception,Json,Serialization,Json.net,Portable Class Library,Missingmethodexception,在PCLs中使用TypeNameAssemblyFormat是否有问题?使用Newtonsoft.Json的任何其他设置都没有问题,除非使用此序列化设置 以下是我的Json相关代码: var settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Objects, Formatting = Formatting.Indented,
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
Formatting = Formatting.Indented,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
};
var json = JsonConvert.SerializeObject(obj, settings);
var jsonBytes = Encoding.UTF8.GetBytes(json);
return jsonBytes;
当我在声明它的同一个库中进行调用时,一切正常。但是,当我从调用上述代码的另一个PCL进行调用时,我得到了missing方法异常。这只在我使用TypeNameAssemblyFormat设置时发生(即,如果我不必使用该设置,那么我就不会写这篇文章;)
我正在使用PCL配置文件7
异常(我不想重复整个堆栈跟踪,但如果有人认为这会有所帮助,我可以这样做):
尽管问题中没有足够的信息来100%确定根本原因。。就个人而言,经过一些实验后,我确信唯一合理的解释如下- 简而言之-在失败的测试中,
Newtonsoft.Json.dll的正确(可移植)版本
未被加载
在long中-正在执行两项测试
NewtonSoft.Json.dll的portable版本
NewtonSoft.Json.dll的一个版本(哪个版本?)
NewtonSoft.Json.dll进行了间接的调用。相反,问题是,正如我试图在上面强调的那样,第二个测试恰好是以某种方式设置的,即PCL1可以使用错误/不可移植的版本的NewtonSoft.Json.dll
在失败的场景中,假设该应用程序的exe或任何其他不可移植程序集也依赖于(不可移植)版本的NewtonSoft.Json.dll
。在这种情况下,在应用程序/exe的输出文件夹中,只有一个版本的NewtonSoft.Json.dll
,如果它是不可移植的,那么它将因上述异常而失败
进一步解释-原因?
类型System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
通常在mscorlib.dll
中定义。但是,此类型不可供可移植类库使用(不知道所有概要文件,但确实有一些概要文件没有此类型)。因此,NewtonSoft.Json.dll的可移植版本在自己的程序集中声明了它自己
在您最喜欢的反编译器中检查反编译版本的portableNewtonSoft.Json.dll
。
注意下面的第3行。。以下代码段来自NewtonSoft.Json.dll
// Decompiled with JetBrains decompiler
// Type: System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
// Assembly: Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
// Assembly location: C:\Users\.....\packages\Newtonsoft.Json.6.0.7\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll
// Compiler-generated code is shown
namespace System.Runtime.Serialization.Formatters
{
/// <summary>
/// Indicates the method that will be used during deserialization for locating and loading assemblies.
///
/// </summary>
public enum FormatterAssemblyStyle
{
Simple,
Full,
}
}
请注意如何使用程序集名称[Newtonsoft.Json]
限定对类型的引用(向右滚动------>以在传递的FormatterAssemblyStyle
参数上查看它)
现在,如果此可移植版本的Newtonsoft.Json.dll
,被替换为非可移植版本(因为项目的其他部分引用了非可移植版本),那么在运行时,CLR将无法找到与上述签名匹配的方法(,如上面的IL所示)。。并因此失败,导致System.MissingMethodException
不幸的是,异常本身没有提供足够的关于它所寻找的方法的完整和准确签名的信息,包括程序集名称。。仅类型名一项就看起来像一个系统dll中存在的东西(mscorlib.dll
)。。而且不是可移植版本的Newtonsoft.json.dll
好的,我已经完成了我自己问题的答案,因为Vikas对问题的框架清晰明了。解决这类问题的方法是标准的PCL方法:创建接口、配置容器、使用DI
// Decompiled with JetBrains decompiler
// Type: System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
// Assembly: Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
// Assembly location: C:\Users\.....\packages\Newtonsoft.Json.6.0.7\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll
// Compiler-generated code is shown
namespace System.Runtime.Serialization.Formatters
{
/// <summary>
/// Indicates the method that will be used during deserialization for locating and loading assemblies.
///
/// </summary>
public enum FormatterAssemblyStyle
{
Simple,
Full,
}
}
在本例中,在我的PCL中,我创建了一个INewtonsoftJsonSettingsProvider接口,该接口具有以下两个设置作为属性:
public interface INewtonsoftJsonSettingsProvider
{
JsonSerializerSettings Default { get; set; }
JsonSerializerSettings Concrete { get; set; }
}
public class NewtonsoftJsonSettingsProvider : Interfaces.INewtonsoftJsonSettingsProvider
{
public JsonSerializerSettings Default { get; set; }
public JsonSerializerSettings Concrete { get; set; }
}
void UseNewtonsoft()
{
var defaultNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
//hack: FormatterAssemblyStyle exists in two dlls and extern alias doesn't work in xamarin studio
var assmNewtonsoft = System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.ConstructorHandling));
var enumType = assmNewtonsoft.GetType("System.Runtime.Serialization.Formatters.FormatterAssemblyStyle");
dynamic enumInstance = Enum.Parse(enumType, "Full");
var concreteNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented,
//TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full //set using dynamic
};
dynamic dynSettings = concreteNewtonsoftSettings;
dynSettings.TypeNameAssemblyFormat = enumInstance;
commonGib.Serialization.NewtonsoftJsonSettingsProvider.CreateAndRegisterWithContainerIdem(defaultNewtonsoftSettings, concreteNewtonsoftSettings);
commonGib.Serialization.NewtonsoftJsonSerializer.RegisterAsSerializerInContainer();
}
然后,在我的PCL中,我创建了该类的具体实现,如下所示:
public interface INewtonsoftJsonSettingsProvider
{
JsonSerializerSettings Default { get; set; }
JsonSerializerSettings Concrete { get; set; }
}
public class NewtonsoftJsonSettingsProvider : Interfaces.INewtonsoftJsonSettingsProvider
{
public JsonSerializerSettings Default { get; set; }
public JsonSerializerSettings Concrete { get; set; }
}
void UseNewtonsoft()
{
var defaultNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
//hack: FormatterAssemblyStyle exists in two dlls and extern alias doesn't work in xamarin studio
var assmNewtonsoft = System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.ConstructorHandling));
var enumType = assmNewtonsoft.GetType("System.Runtime.Serialization.Formatters.FormatterAssemblyStyle");
dynamic enumInstance = Enum.Parse(enumType, "Full");
var concreteNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented,
//TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full //set using dynamic
};
dynamic dynSettings = concreteNewtonsoftSettings;
dynSettings.TypeNameAssemblyFormat = enumInstance;
commonGib.Serialization.NewtonsoftJsonSettingsProvider.CreateAndRegisterWithContainerIdem(defaultNewtonsoftSettings, concreteNewtonsoftSettings);
commonGib.Serialization.NewtonsoftJsonSerializer.RegisterAsSerializerInContainer();
}
注意:我可以轻松跳过接口,只使用这个helper类,但我喜欢在处理容器时使用接口
然后,在存在Newtonsoft序列化程序的PCL中,我使用容器中的设置,而不是直接在序列化方法中创建这些设置。我将继续并在这里包含该代码(由于这个问题,我将序列化抽象为一个接口,因此我可以交换实现):
这在我的.Net测试项目中执行时没有问题。然而,当我在Xamarin.Android项目中使用它时,我得到了一个错误,指出FormatterAssemblyStyle在Newtonsoft和MonoAndroid mscorlib中都存在。由于Xamarin Studio似乎不使用外部别名,因此我使用了反射和动态,如下所示:
public interface INewtonsoftJsonSettingsProvider
{
JsonSerializerSettings Default { get; set; }
JsonSerializerSettings Concrete { get; set; }
}
public class NewtonsoftJsonSettingsProvider : Interfaces.INewtonsoftJsonSettingsProvider
{
public JsonSerializerSettings Default { get; set; }
public JsonSerializerSettings Concrete { get; set; }
}
void UseNewtonsoft()
{
var defaultNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
//hack: FormatterAssemblyStyle exists in two dlls and extern alias doesn't work in xamarin studio
var assmNewtonsoft = System.Reflection.Assembly.GetAssembly(typeof(Newtonsoft.Json.ConstructorHandling));
var enumType = assmNewtonsoft.GetType("System.Runtime.Serialization.Formatters.FormatterAssemblyStyle");
dynamic enumInstance = Enum.Parse(enumType, "Full");
var concreteNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Objects,
Formatting = Newtonsoft.Json.Formatting.Indented,
//TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full //set using dynamic
};
dynamic dynSettings = concreteNewtonsoftSettings;
dynSettings.TypeNameAssemblyFormat = enumInstance;
commonGib.Serialization.NewtonsoftJsonSettingsProvider.CreateAndRegisterWithContainerIdem(defaultNewtonsoftSettings, concreteNewtonsoftSettings);
commonGib.Serialization.NewtonsoftJsonSerializer.RegisterAsSerializerInContainer();
}
在您的测试中,当您遇到上述异常时,Newtonsoft.Json
是否仅从它使用的PCL引用,或者是否有其他非PCL程序集(甚至可能是具有不同配置文件的PCL程序集)也引用了Newtonsoft.Json
。换句话说,当此代码不起作用时,所有程序集都引用了哪些Newtonsoft.Json
,它们的配置文件是什么?另一方面,当它工作时,是Newtonsoft.Json