在 Azure 函数中序列化
Serializing in Azure Function
我 运行 遇到了 Azure Function Apps 的一个奇怪问题。 Newtonsoft Json.NET 反序列化不喜欢 $type
注释。我的反序列化代码如下所示:
return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto
});
json 看起来像:
{
"$type": "Trading.Control.Json.TradingConfig, Trading",
"Config": {
"$type": "Trading.Control.Json.Config, Trading",
"Optimize": false
},
"Trading": {
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Trading.Platforms.Credentials, Trading]], mscorlib",
...
并序列化为:
return JsonConvert.SerializeObject(o, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented
});
错误是:
2017-08-01T17:32:46.395 Type specified in JSON
'Trading.Control.Json.TradingConfig, Trading, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with
'Trading.Control.Json.TradingConfig, Trading, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Path '$type', line 2, position 56.
如您所见,类型似乎相同。此代码在本地经过良好测试并按预期工作。它会在遇到第一个 $type
注释时在 Azure 中失败,无论我删除了多少。
我想继续使用注释,因为我需要它们来反序列化从抽象派生的对象 class。
这是在 x64、.NET 4.7、Json.NET v10.0.3、Azure Function Apps v1.0.11027.0 (~1) 中编译的。我在 bin 文件夹中有 Newtonsoft.Json.dll 文件,用 #r "Newtonsoft.Json.dll"
来引用它。有任何想法吗?非常感谢。
编辑:
我还尝试添加一个 project.json 文件,如下所示:
{
"frameworks": {
"net47":{
"dependencies": {
"Newtonsoft.Json": "10.0.3"
}
}
}
}
安装成功。我删除了上传的程序集文件和 #r
导入。现在的错误是:
2017-08-01T18:30:18.971 Error resolving type specified in JSON 'Trading.Control.Json.TradingConfig, Trading'. Path '$type', line 2, position 56.
我怀疑存在 "base namespace" 或类似的查找错误。
该函数的文件系统如下所示:/site/wwwroot/TimerTriggerCSharp3/
程序集位于 bin 文件夹中。它们都加载了 #r
导入,工作正常。
我遇到了同样的问题,并使用 SerializationBinder 解决了它。这是因为函数运行时加载程序集的加载上下文
下面的代码让您可以获取任意程序集名称并解析它。您可以将其与序列化设置一起传递。所以你可以检查交易程序集。 class 上的评论说明为什么有必要
a => a.GetName().Name == "Trading" ? typeof(Trading.Control.Json.TradingConfig).Assembly : null;
/// <summary>
/// Uses the func to resolve assembly instances by name, since they may be in a different directory and not
/// directly resolvable by Assembly.Load (the default method used by JSON.NET)
/// </summary>
internal class SerializationBinder : DefaultSerializationBinder
{
private readonly Func<string, Assembly> assemblyResolver;
public SerializationBinder(Func<string, Assembly> assemblyResolver)
{
this.assemblyResolver = assemblyResolver;
}
public override Type BindToType(string assemblyName, string typeName)
{
Assembly assembly;
try
{
assembly = assemblyResolver(assemblyName);
}
catch
{
// not registered
return base.BindToType(assemblyName, typeName);
}
var type = assembly.GetType(typeName);
if (type == null)
type = GetGenericTypeFromTypeName(typeName, assembly);
if (type != null) return type;
return base.BindToType(assemblyName, typeName);
}
/// <summary>
/// From DefaultSerializationBinder.
/// </summary>
/// <param name="typeName"></param>
/// <param name="assembly"></param>
/// <returns></returns>
private Type GetGenericTypeFromTypeName(string typeName, Assembly assembly)
{
Type type1 = null;
var length = typeName.IndexOf('[');
if (length >= 0)
{
var name = typeName.Substring(0, length);
var type2 = assembly.GetType(name);
if (type2 != null)
{
var typeList = new List<Type>();
var num1 = 0;
var startIndex = 0;
var num2 = typeName.Length - 1;
for (var index = length + 1; index < num2; ++index)
switch (typeName[index])
{
case '[':
if (num1 == 0)
startIndex = index + 1;
++num1;
break;
case ']':
--num1;
if (num1 == 0)
{
typeName = SplitFullyQualifiedTypeName(typeName.Substring(startIndex, index - startIndex));
return Type.GetType(typeName);
}
break;
}
type1 = type2.MakeGenericType(typeList.ToArray());
}
}
return type1;
}
/// <summary>
/// From Reflectionutils
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static string SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
{
var assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
string typeName;
if (assemblyDelimiterIndex.HasValue)
typeName = Trim(fullyQualifiedTypeName, 0, assemblyDelimiterIndex.GetValueOrDefault());
else
typeName = fullyQualifiedTypeName;
return typeName;
}
/// <summary>
/// From Reflectionutils
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
{
var num = 0;
for (var index = 0; index < fullyQualifiedTypeName.Length; ++index)
switch (fullyQualifiedTypeName[index])
{
case ',':
if (num == 0)
return index;
break;
case '[':
++num;
break;
case ']':
--num;
break;
}
return new int?();
}
private static string Trim(string s, int start, int length)
{
if (s == null)
throw new ArgumentNullException();
if (start < 0)
throw new ArgumentOutOfRangeException("start");
if (length < 0)
throw new ArgumentOutOfRangeException("length");
var index = start + length - 1;
if (index >= s.Length)
throw new ArgumentOutOfRangeException("length");
while (start < index && char.IsWhiteSpace(s[start]))
++start;
while (index >= start && char.IsWhiteSpace(s[index]))
--index;
return s.Substring(start, index - start + 1);
}
}
`
必须更改这部分才能使其正常工作
if (num1 == 0)
{
var typeArgumentName = SplitFullyQualifiedTypeName(typeName.Substring(startIndex, index - startIndex));
typeList.Add(Type.GetType(typeArgumentName));
}
我 运行 遇到了 Azure Function Apps 的一个奇怪问题。 Newtonsoft Json.NET 反序列化不喜欢 $type
注释。我的反序列化代码如下所示:
return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.Auto
});
json 看起来像:
{
"$type": "Trading.Control.Json.TradingConfig, Trading",
"Config": {
"$type": "Trading.Control.Json.Config, Trading",
"Optimize": false
},
"Trading": {
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[Trading.Platforms.Credentials, Trading]], mscorlib",
...
并序列化为:
return JsonConvert.SerializeObject(o, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All,
Formatting = Formatting.Indented
});
错误是:
2017-08-01T17:32:46.395 Type specified in JSON
'Trading.Control.Json.TradingConfig, Trading, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with
'Trading.Control.Json.TradingConfig, Trading, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Path '$type', line 2, position 56.
如您所见,类型似乎相同。此代码在本地经过良好测试并按预期工作。它会在遇到第一个 $type
注释时在 Azure 中失败,无论我删除了多少。
我想继续使用注释,因为我需要它们来反序列化从抽象派生的对象 class。
这是在 x64、.NET 4.7、Json.NET v10.0.3、Azure Function Apps v1.0.11027.0 (~1) 中编译的。我在 bin 文件夹中有 Newtonsoft.Json.dll 文件,用 #r "Newtonsoft.Json.dll"
来引用它。有任何想法吗?非常感谢。
编辑: 我还尝试添加一个 project.json 文件,如下所示:
{
"frameworks": {
"net47":{
"dependencies": {
"Newtonsoft.Json": "10.0.3"
}
}
}
}
安装成功。我删除了上传的程序集文件和 #r
导入。现在的错误是:
2017-08-01T18:30:18.971 Error resolving type specified in JSON 'Trading.Control.Json.TradingConfig, Trading'. Path '$type', line 2, position 56.
我怀疑存在 "base namespace" 或类似的查找错误。
该函数的文件系统如下所示:/site/wwwroot/TimerTriggerCSharp3/
程序集位于 bin 文件夹中。它们都加载了 #r
导入,工作正常。
我遇到了同样的问题,并使用 SerializationBinder 解决了它。这是因为函数运行时加载程序集的加载上下文
下面的代码让您可以获取任意程序集名称并解析它。您可以将其与序列化设置一起传递。所以你可以检查交易程序集。 class 上的评论说明为什么有必要
a => a.GetName().Name == "Trading" ? typeof(Trading.Control.Json.TradingConfig).Assembly : null;
/// <summary>
/// Uses the func to resolve assembly instances by name, since they may be in a different directory and not
/// directly resolvable by Assembly.Load (the default method used by JSON.NET)
/// </summary>
internal class SerializationBinder : DefaultSerializationBinder
{
private readonly Func<string, Assembly> assemblyResolver;
public SerializationBinder(Func<string, Assembly> assemblyResolver)
{
this.assemblyResolver = assemblyResolver;
}
public override Type BindToType(string assemblyName, string typeName)
{
Assembly assembly;
try
{
assembly = assemblyResolver(assemblyName);
}
catch
{
// not registered
return base.BindToType(assemblyName, typeName);
}
var type = assembly.GetType(typeName);
if (type == null)
type = GetGenericTypeFromTypeName(typeName, assembly);
if (type != null) return type;
return base.BindToType(assemblyName, typeName);
}
/// <summary>
/// From DefaultSerializationBinder.
/// </summary>
/// <param name="typeName"></param>
/// <param name="assembly"></param>
/// <returns></returns>
private Type GetGenericTypeFromTypeName(string typeName, Assembly assembly)
{
Type type1 = null;
var length = typeName.IndexOf('[');
if (length >= 0)
{
var name = typeName.Substring(0, length);
var type2 = assembly.GetType(name);
if (type2 != null)
{
var typeList = new List<Type>();
var num1 = 0;
var startIndex = 0;
var num2 = typeName.Length - 1;
for (var index = length + 1; index < num2; ++index)
switch (typeName[index])
{
case '[':
if (num1 == 0)
startIndex = index + 1;
++num1;
break;
case ']':
--num1;
if (num1 == 0)
{
typeName = SplitFullyQualifiedTypeName(typeName.Substring(startIndex, index - startIndex));
return Type.GetType(typeName);
}
break;
}
type1 = type2.MakeGenericType(typeList.ToArray());
}
}
return type1;
}
/// <summary>
/// From Reflectionutils
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static string SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
{
var assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
string typeName;
if (assemblyDelimiterIndex.HasValue)
typeName = Trim(fullyQualifiedTypeName, 0, assemblyDelimiterIndex.GetValueOrDefault());
else
typeName = fullyQualifiedTypeName;
return typeName;
}
/// <summary>
/// From Reflectionutils
/// </summary>
/// <param name="fullyQualifiedTypeName"></param>
/// <returns></returns>
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
{
var num = 0;
for (var index = 0; index < fullyQualifiedTypeName.Length; ++index)
switch (fullyQualifiedTypeName[index])
{
case ',':
if (num == 0)
return index;
break;
case '[':
++num;
break;
case ']':
--num;
break;
}
return new int?();
}
private static string Trim(string s, int start, int length)
{
if (s == null)
throw new ArgumentNullException();
if (start < 0)
throw new ArgumentOutOfRangeException("start");
if (length < 0)
throw new ArgumentOutOfRangeException("length");
var index = start + length - 1;
if (index >= s.Length)
throw new ArgumentOutOfRangeException("length");
while (start < index && char.IsWhiteSpace(s[start]))
++start;
while (index >= start && char.IsWhiteSpace(s[index]))
--index;
return s.Substring(start, index - start + 1);
}
}
`
必须更改这部分才能使其正常工作
if (num1 == 0)
{
var typeArgumentName = SplitFullyQualifiedTypeName(typeName.Substring(startIndex, index - startIndex));
typeList.Add(Type.GetType(typeArgumentName));
}