2017-08-01 1 views
1

Ich habe ein seltsames Problem mit Azure-Funktions-Apps. Die Deserialisierung von Newtonsoft Json.NET gefällt den $type Annotationen nicht. Mein Deserialisierung Code wie folgt aussieht:Serialisierung in Azure-Funktion

return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings { 
    TypeNameHandling = TypeNameHandling.Auto 
}); 

Die json wie folgt aussieht:

{ 
    "$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", 
... 

Und serialisiert mit:

return JsonConvert.SerializeObject(o, new JsonSerializerSettings { 
    TypeNameHandling = TypeNameHandling.All, 
    Formatting = Formatting.Indented 
}); 

Der Fehler ist:

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. 

Wie Sie kann sehen, die Typen scheinen identisch zu sein l. Dieser Code ist lokal gut getestet und funktioniert wie erwartet. Es wird in Azure auf der ersten $type Annotation fehlschlagen, unabhängig davon, wie viele ich entferne.

Ich möchte weiterhin die Anmerkungen verwenden, da ich sie für das Deserialisieren von Objekten benötigen, die von einer abstrakten Klasse abgeleitet sind.

Dies ist in x64, .NET 4.7, Json.NET v10.0.3, Azure Function Apps v1.0.11027.0 (~ 1) kompiliert. Ich habe die Newtonsoft.Json.dll-Datei im Ordner bin, mit #r "Newtonsoft.Json.dll", um es zu verweisen. Irgendwelche Ideen? Sehr geschätzt.

Edit: Ich habe auch eine project.json Datei suchen, wie versucht, indem:

{ 
    "frameworks": { 
    "net47":{ 
     "dependencies": { 
     "Newtonsoft.Json": "10.0.3" 
     } 
    } 
    } 
} 

, die erfolgreich installiert. Ich habe die hochgeladene Assembly-Datei und den Import #r entfernt. Der Fehler ist nun:

2017-08-01T18:30:18.971 Error resolving type specified in JSON 'Trading.Control.Json.TradingConfig, Trading'. Path '$type', line 2, position 56. 

Ich vermute, es ist ein „Basis-Namensraum“, etc. zu jagen Lookup-Fehler.

Das Dateisystem der Funktion sieht folgendermaßen aus: /site/wwwroot/TimerTriggerCSharp3/ mit Assemblys in einem bin-Ordner. Sie sind alle mit #r Importe geladen, die gut funktionieren.

+0

Sie höchstwahrscheinlich zwei Kopien der DLL geladen funktioniert. Bitte schauen Sie sich den App Service Editor an und fügen Sie von dort eine Beschreibung der Funktion app Dateisystem Struktur hinzu. Machst du gerade eine Montage? – Jeff

+0

Verwenden Sie auch csx oder eine vorkompilierte Funktion dll? – Jeff

+0

@Jeff Ich benutze csx. Ich hatte meine eigenen Assemblys geladen, aber die Datei und den Import nach dem Hinzufügen der Datei project.json entfernt. Ich erhalte jetzt einen anderen Fehler (oben aktualisiert). – Osan

Antwort

3

Ich hatte das gleiche Problem und löste es mit einem SerializationBinder. Dies geschieht aufgrund des Ladekontexts von Baugruppen, die von der Laufzeit-Funktion

geladen werden. Mit dem folgenden Code können Sie einen beliebigen Assemblynamen verwenden und ihn auflösen. Sie können es mit den Serialisierungseinstellungen übergeben. So könnten Sie nach der Handelsversammlung suchen. Der Kommentar von der Klasse, warum es notwendig ist

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 Paket automatisch von der Laufzeit referenziert wird. Sie sollten das nicht manuell tun, sonst könnten Sie einen Versionskonflikt bekommen. Just do

#r "Newtonsoft.Json" 

Wenn das nicht das Problem gelöst wird, veröffentlichen Sie bitte die minimal Beispiel vollständig das Problem zu reproduzieren, einschließlich der C# Klasse.

+0

Das wird nicht helfen. Es ist eine inhärente Inkompatibilität zwischen der Assembly-Auflösung von Json .net und der Laufzeit der Azure-Funktion. – Jeff

0

Hatte diesen Teil zu ändern, um es

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