Is there a way to store instances of own classes in the ApplicationSettings of a Windows Store app? - configuration

In a Windows Store app I can only store WinRT types in the ApplicationSettings, according to the documentation. For roamed settings that should be held together I can use ApplicationDataCompositeValue. Trying to store an instance of an own class or struct results in an Exception with the message " WinRT information: Error trying to serialize the value to be written to the application data store. Additional Information: Data of this type is not supported". The term "trying to serialize" indicates that there must be some way so serialize a type for the application data API.
Does anyone know how I could achieve that?
I tried DataContract serialization but it did not work.

I think custom/own types are not supported.
See http://msdn.microsoft.com/en-us/library/windows/apps/hh464917.aspx:
"The Windows Runtime data types are supported for app settings."
But you can serialize your objects to XML and save as string... (see code below)
public static string Serialize(object obj)
{
using (var sw = new StringWriter())
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(sw, obj);
return sw.ToString();
}
}
public static T Deserialize<T>(string xml)
{
using (var sw = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(sw);
}
}
https://github.com/MyToolkit/MyToolkit/blob/master/src/MyToolkit/Serialization/XmlSerialization.cs
Check out this class too:
https://github.com/MyToolkit/MyToolkit/wiki/XmlSerialization
Disclaimer: The above links are from my project

Related

.NET Core console app dependency injection - how to configure usage of Newtonsoft.Json for deserializing IOptions<>

Is it possible to configure a .Net Core 3.1 console application to use the Newtonsoft.Json library to deserialize the IOptions<> (from Microsoft.Extensions.Options) , when reading the sections of a json settings file?
public MyService(IOptions<MyAppDataSettings> MyAppDataOptions)
{
var myAppDataOptions = MyAppDataOptions?.Value ?? throw new ArgumentNullException(nameof(MyAppDataSettings));
}
The result is different from when deserializing directly with System.Text.Json:
var appJsonData = System.Text.Json.JsonSerializer.Deserialize<MyAppDataSettings>(File.ReadAllText(appJsonPath));
The settings contain an "ordered" dictionary. When calling directly the deserializer, the order of the keys is correct, as in the json settings file. But the value returned by IOptions<> contains a dictionary with the keys sorted alphabetically.
I tried then to force the usage of NewtonsoftJson:
private static void ConfigureServices(IConfiguration configuration, IServiceCollection services)
{
services.AddControllers().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
services.Configure<MyAppDataSettings>(configuration.GetSection(nameof(MyAppDataSettings)));
services.AddSingleton<IMyService, MyService>();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
new HostBuilder()
.ConfigureServices((context, services) =>
{
ConfigureServices(context.Configuration, services);
})
.ConfigureAppConfiguration((context, configurationBuilder) =>
{
var appExecPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
var appSettingsPath = Path.GetFullPath(Path.Combine(appExecPath, #"Settings"));
configurationBuilder
.SetBasePath(appSettingsPath)
.AddJsonFile("MySettings.json", false);
});
The result when using the Newtonsoft.Json deserializer is the same as with System.Text.Json.
var appJsonData = JsonConvert.DeserializeObject<MyAppDataSettings>(File.ReadAllText(appJsonPath));
So the question is: how are the IOptions< MyAppDataSettings > actually deserialized? I suppose that the "magic" occurs in the Microsoft.Extensions.Configuration.Json library.
Neither JSON nor Dictionary guarantees key ordering. As per Dictionary documentation
MSDN Dictionary:
For purposes of enumeration, each item in the dictionary is treated as a KeyValuePair structure representing a value and its key. The order in which the items are returned is undefined.
Based on my experience I would highly recommend to rethink the approach you uses for configuration and get rid of order dependency.

Is there a work-a-round to serializing some Microsoft Graph Entities with interfaces like Domain

I wanted to generate a test program to execute against our client tenants to verify we could handle all the data our new Microsoft graph app collects. My plan was to serialize the data using
XmlSerializer serializer = new XmlSerializer(typeof(List<T>));
It failed on the first entity I tried, Microsoft.Graph.Domain ( in this case with the error
Cannot serialize member Microsoft.Graph.Entity.AdditionalData of type ... because it is an interface.
A search on stack overflow found suggestions to decorate the problematic class property with XmlIgnore so XmlSerializer will ignore it, others recommended implementing a new IXmlSerializer. One post seemed to propose using serializing to XAML.
Open to a better way to collect real customer data which I can import into my unit tests? As a developer I do not have direct access to customer accounts.
Does anyone have other suggestions on how to serialize Microsoft Graph Entities.
I replaced my XmlSerializer with a Json one.
public void SerializeObjectsToJson<T>(List<T> serializableObjects)
{
var jsonStr = JsonConvert.SerializeObject(serializableObjects);
}
public List<T> DeSerializeObjectsFromXml<T>()
{
TextReader textReader = new StreamReader(fqpathname, Encoding.UTF8);
var jsonStr = textReader.ReadToEnd();
data = JsonConvert.DeserializeObject<List<T>>(jsonStr);
}
This all seems to work with Domain, User, SubscribedSkus, Organization, etc.

Xamarin Forms - Saving object to access it later

I am using Xamarin Forms with Newtonsoft.JSON and Xam.Plugin.Settings plugins to save IDevice object as JSON to use it later.
First page:
private async void SelectBluetoothDevice(object sender, SelectedItemChangedEventArgs e)
{
string device = JsonConvert.SerializeObject((IDevice) e.SelectedItem);
AppSettings.AddOrUpdateValue("device", device);
await Navigation.PopAsync();
}
Here I simply make selected item to JSON string and save it. It works like it should be.
But the problem im facing comes whenever I try to deserialize from saved string.
string device = AppSettings.GetValueOrDefault("device", "");
if (!device.Equals(""))
{
Debug.WriteLine(device);
// This line produces error
IDevice dev = JsonConvert.DeserializeObject<IDevice>(device);
settingsDeviceName.Text = dev.Name;
}
Newtonsoft.Json.JsonSerializationException: Could not create an
instance of type Plugin.BLE.Abstractions.Contracts.IDevice. Type is an
interface or abstract class and cannot be instantiated. Path
'BluetoothDevice', line 1, position 19.
So I understand that IDevice is interface and my JSON string cant be deserialized nothing but into object. Any good ideas how to workaround it? Thanks!
This line JsonConvert.DeserializeObject<IDevice>(device); basically does this:
Read the JSON in device
Try to create the type of object you want to deserialize to, in this case, IDevice
Return you the new object with all the properties filled
The problem is with step 2. You can't create an instance of an interface. So you need to deserialize to a concrete object that implements IDevice.

Jersey/Genson: Unmarschalling single object array

Similar to Jersey: Json array with 1 element is serialized as object BUT on the client side. E.g. I recieve a JSON object where a field is an array regulary, but in case there is only one element, it is a single object.
{"fileInfo":[{"fileName":"weather.arff","id":"10"},"fileName":"supermarket.arff","id":"11"}]}
versus
{"fileInfo":{"fileName":"weather.arff","id":"10"}}
I'm parsing/unmarshalling the JSON using Jersey/Genson. Of course, if the JSON doesnt match the target class I recieve an error (such as expected [ but read '{' )
I've read a lot about this bug and how to avoid when creating JSON objects on the SERVER side, but I found nothing about how to handle this issus when dealing on the CLIENT side.
As always, I prefere the most codeless possibility if there are several solutions...
BTW: Moxy works but it does not marshal native Object-type objects which is another requirement...
Update
Starting with Genson 1.3 release you can achieve it by enabling permissiveParsing:
Genson genson = new GensonBuilder().usePermissiveParsing(true).create();
Answer
Uh, do you know what library produces this on server side? I am curious to see who is responsible for all those badly structured jsons out there...
It is not yet supported in Genson. Originally because IMO people should not produce such dynamic json. Anyway, I opened an issue - this can be easily done, you can expect it to be present in the release coming next week.
Otherwise here is a way to achieve it without breaking the existing mechanisms.
You need to register a Factory that will use Gensons collections factory to create an instance of its standard collection converter. Then you will wrap this converter in another one that will handle the object to array logic. Here is the code (not codeless..., but if you wait a bit you won't have to code :)).
import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
import com.owlike.genson.convert.DefaultConverters.CollectionConverterFactory;
class SingleObjectAsCollectionFactory implements Factory<Converter<Collection>> {
// get the default factory
Factory<Converter<Collection<?>>> defaultFactory = CollectionConverterFactory.instance;
#Override
public Converter<Collection> create(Type type, Genson genson) {
// obtain an instance of the correct default converter for this type
final CollectionConverter defaultConverter = (CollectionConverter) defaultFactory.create(type, genson);
// wrap it in your own converter
return new Converter<Collection>() {
#Override
public void serialize(Collection object, ObjectWriter writer, Context ctx) throws Exception {
defaultConverter.serialize(object, writer, ctx);
}
#Override
public Collection deserialize(ObjectReader reader, Context ctx) throws Exception {
if (reader.getValueType() == ValueType.OBJECT) {
Object object = defaultConverter.getElementConverter().deserialize(reader, ctx);
Collection result = defaultConverter.create();
result.add(object);
return result;
} else return defaultConverter.deserialize( reader, ctx );
}
};
}
}
And then register it
Genson genson = new GensonBuilder()
.withConverterFactory(new SingleObjectAsCollectionFactory())
.create();

WinRT XmlAnyElement and Serialization

We have a Windows Store application that communicates with our server using XML for requests / responses and are serialized with the XmlSerializer. The issue we are encountering is that one of our types can contain arbitrary XML as one of its properties. In non WinRT applications, the usage would have been.
public sealed ItemExtension {
[XmlAttribute("source")]
public string Source {get;set;}
[XmlAnyElement]
public XmlElement[] Data {get;set;}
}
This would allow us to have XML in our database like
<extension source="foo"><randomXml><data/></randomXml></extension>
In WinRT, XmlElement is not included, System.Xml.XmlElement does not exist and the Windows.Data.Xml.Dom.XmlElement is not compatible. Documentation mentions XElement, but XElement is not a supported WinRT type so the WinRT project won't compile if I try using it.
Is this a bug with Windows Store applications or is there a sufficient work around?
Thanks.
So far I've only found a hack to get this working. If we use
[XmlAnyElement]
public object Data {get;set;}
This will properly will properly deserialize existing data. In the debugger when inspecting it, it is of type System.Xml.XmlElement, which isn't exposed in WinRT. So there's no way to directly set this. Since we figured out that XmlSerializer can instantiate and access System.Xml.XmlElement, we use it to handle setting it by taking in an object/xml snippet, wrapping it in container xml for a wrapper type that contains [XmlAnyElement] and calling Deserialize on it to have the XmlSerializer instantiate an XmlElement, which can then be set on the target object you wish to serialize.
For getting data, since trying to read this property throws an exception in the UI layer, and trying to access InnerXml/OuterXml throw an exception as well, we are left with using the XmlSerializer to Serialize the XmlElement back into a string, and then can use that however you want.
public sealed class XmlAnyElementContainer
{
[XmlAnyElement]
public object Data { get; set; }
}
public void SetData(object extensionObject)
{
var objectSerializer = new XmlSerializer(extensionObject.GetType());
var settings = new XmlWriterSettings()
{
Indent = false,
OmitXmlDeclaration = true
};
var sb = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(sb, settings))
{
objectSerializer.Serialize(xmlWriter, extensionObject);
}
string objectXml = sb.ToString();
string newXml = "<XmlAnyElementContainer>" + objectXml + "</XmlAnyElementContainer>";
var xmlAnySerializer = new XmlSerializer(typeof(XmlAnyElementContainer));
using (var sr = new StringReader(newXml))
{
[TargetPropertyToSerialize] = (xmlAnySerializer.Deserialize(sr) as XmlAnyElementContainer).Data;
}
}