Ok, ho finito per completare la risposta della mia stessa domanda a causa della chiarezza dell'inquadramento del problema da parte di Vikas. E la risoluzione è l'approccio PCL standard con questo tipo di problema: Crea interfaccia, configura contenitore, usa DI.
In questo caso, nel mio PCL, ho creato un'interfaccia INewtonsoftJsonSettingsProvider che ha le due impostazioni che uso come proprietà come segue:
public interface INewtonsoftJsonSettingsProvider
{
JsonSerializerSettings Default { get; set; }
JsonSerializerSettings Concrete { get; set; }
}
Poi, nel mio PCL, creo una concreta implementazione di questa classe come segue:
public class NewtonsoftJsonSettingsProvider : Interfaces.INewtonsoftJsonSettingsProvider
{
public JsonSerializerSettings Default { get; set; }
public JsonSerializerSettings Concrete { get; set; }
}
Nota: ho potuto facilmente saltare l'interfaccia e basta usare questa classe di supporto da sola, ma io piace usare le interfacce quando si tratta del contenitore.
Poi, nel mio PCL dove esiste il serializzatore Newtonsoft, che consumano le impostazioni dal contenitore invece di creare direttamente quelli all'interno dei metodi di serializzazione. Vado avanti e comprendono che il codice anche qui (I Sottratto serializzazione a un'interfaccia a causa di questo problema, in modo da poter scambiare le implementazioni):
public class NewtonsoftJsonSerializer : ICustomSerializer
{
public static void RegisterAsSerializerInContainer()
{
var key = Resx.DIKeys.DefaultSerializer;
var typeContract = typeof(ICustomSerializer);
if (DI.Ton.KeyExists(key))
{
var instance = DI.Ton.Get(typeContract, key);
DI.Ton.RemoveInstance(key, instance, typeContract);
}
DI.Ton.AddImplementationType(typeof(ICustomSerializer),
typeof(NewtonsoftJsonSerializer),
isShared: true);
var serializer = new NewtonsoftJsonSerializer();
DI.Ton.AddInstance<ICustomSerializer>(Resx.DIKeys.DefaultSerializer, serializer);
}
/// <summary>
/// This is used to overcome the problem of PCL vs non-PCL versions when using TypeNameAssemblyFormat.
/// see http://stackoverflow.com/questions/27080363/missingmethodexception-with-newtonsoft-json-when-using-typenameassemblyformat-wi
/// </summary>
/// <returns></returns>
private static INewtonsoftJsonSettingsProvider GetSettingsProvider()
{
var key = typeof(INewtonsoftJsonSettingsProvider).Name;
var settings = DI.Ton.GetInstance<INewtonsoftJsonSettingsProvider>(key);
return settings;
}
public byte[] SerializeToBytes(object obj)
{
var settings = GetSettingsProvider();
var json = JsonConvert.SerializeObject(obj, settings.Default);
var jsonBytes = Encoding.UTF8.GetBytes(json);
return jsonBytes;
}
public T DeserializeFromBytes<T>(byte[] serializedBytes)
{
var json = Encoding.UTF8.GetString(serializedBytes, 0, serializedBytes.Length);
var settings = GetSettingsProvider();
var obj = JsonConvert.DeserializeObject<T>(json, settings.Default);
return obj;
}
public byte[] SerializeToBytes_UseConcreteTypes(object obj)
{
var settings = GetSettingsProvider();
var json = JsonConvert.SerializeObject(obj, settings.Concrete);
var jsonBytes = Encoding.UTF8.GetBytes(json);
return jsonBytes;
}
public T DeserializeFromBytes_UseConcreteTypes<T>(byte[] serializedBytes)
{
var json = Encoding.UTF8.GetString(serializedBytes, 0, serializedBytes.Length);
var settings = GetSettingsProvider();
var obj = JsonConvert.DeserializeObject<T>(json, settings.Concrete);
return obj;
}
}
Poi, nel mio consumo non-PCL e non Xamarin (può funzionare in PCL, ma Xamarin ha problema - vedi sotto), configura il contenitore con il corretto System.Runtime.Serialization.Formatters.FormatterAssemblyStyle
come spiegato nella risposta Vikas':
private static void UseNewtonsoft()
{
var defaultNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
var concreteNewtonsoftSettings = new Newtonsoft.Json.JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects,
Formatting = Formatting.Indented,
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full
};
Serialization.NewtonsoftJsonSettingsProvider.CreateAndRegisterWithContainerIdem(defaultNewtonsoftSettings, concreteNewtonsoftSettings);
Serialization.NewtonsoftJsonSerializer.RegisterAsSerializerInContainer();
}
Questo esegue senza problemi nei miei progetti di test .Net. Tuttavia, quando sono andato a utilizzarlo nel progetto Xamarin.Android, ho ricevuto un errore che indica che FormatterAssemblyStyle esiste sia in Newtonsoft che in MonoAndroid mscorlib. Dal momento che Xamarin Studio non sembra fare alias extern, ho usato la riflessione e dinamica come segue:
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();
}
nel test, quando si arriva sopra eccezione, si 'Newtonsoft.Json' riferimento ** ** solo dalla prima il PCL in cui è stato utilizzato, oppure ci sono altri assembly non PCL (o potrebbero essere anche assembly PCL con profilo diverso) che fanno riferimento anche a 'Newtonsoft.Json'. In altre parole, quando questo codice non funziona, quali tutti gli assembly fanno riferimento a 'Newtonsoft.Json' e quali sono i loro profili? D'altro canto, quando funziona, 'Newtonsoft.Json' fa riferimento solo a un assembly, il PCL che contiene il codice precedente? –
Newtonsoft fa riferimento a PCL1, PCL2, Test1 e Test2 (.net 4.5). Passa su Test1 che fa riferimento a PCL1, non riesce su Test2 che fa riferimento a PCL1, PCL2. Non penso che il progetto verrebbe compilato se ognuno di questi non avesse fatto riferimento a Newtonsoft. – ibgib
Scratch il commento sopra: Newtonsoft è referenziato da PCL1, PCL2, Test1 e Test2 (.net 4.5). Errore su Test1 e Test2. Ho fatto diversi giorni per risolvere questo problema e non ricordo a che punto ho posto questa domanda SO, e non ricordo le circostanze specifiche di successo/fallimento. Dopo aver ripeterlo alcuni stamattina, sia Test1 che Test2 falliscono. – ibgib