2017-08-01 52 views
1

我遇到了Azure功能應用程序的一個奇怪問題。 Newtonsoft Json.NET反序列化不喜歡$type註釋。我反序列化的代碼如下所示:在Azure功能中序列化

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上失敗。

我想繼續使用註釋,因爲我需要它們來反序列化從抽象類派生的對象。

這是在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. 

我懷疑有一個「基名稱空間」或某些查找錯誤。

該函數的文件系統看起來像:/site/wwwroot/TimerTriggerCSharp3/,程序集位於bin文件夾中。他們全部裝入#r進口,這工作正常。

+0

你最有可能加載的dll的兩個副本。請查看應用服務編輯器,並從那裏添加功能應用程序文件系統結構的描述。你在做任何裝配嗎? – Jeff

+0

你還使用csx或預編譯的函數DLL? – Jeff

+0

@Jeff我正在使用csx。我一直在加載自己的程序集,但在添加project.json文件後刪除了文件和導入。我現在正在得到一個不同的錯誤(更新上面)。 – Osan

回答

3

我有同樣的問題,並使用SerializationBinder解決它。它發生是因爲函數運行時加載的程序集的加載上下文

下面的代碼讓您獲取任意程序集名稱並解析它。您可以使用序列化設置將其傳入。所以你可以檢查交易大會。爲什麼有必要

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); 
    } 
} 

`

0

Newtonsoft.Json NuGet包由運行時自動引用的信中的類註釋。您不應該手動執行此操作,否則可能會發生版本衝突。只是做

#r "Newtonsoft.Json" 

如果這不能解決問題,請發佈最小的例子,以完全重現問題,包括C#類。

+0

這沒有幫助。這是json .net的程序集解析和azure函數運行時間之間固有的不兼容性。 – Jeff

0

不得不改變這一部分,使其工作

      if (num1 == 0) 
          { 
           var typeArgumentName = SplitFullyQualifiedTypeName(typeName.Substring(startIndex, index - startIndex)); 
           typeList.Add(Type.GetType(typeArgumentName)); 
          }