Benutzer mit den meisten Antworten
XML-Austausch

Frage
-
Guten Morgen,
ich verzweifle hier so langsam. Ist einem von euch, es schon mal gelungen, mit Bordmitteln, also ohne Drittanbieter, externen DLLs etc. ein Objekt (Klasse) in einer Desktop-App als XML zu serialisieren und diese auf einem "entfernten" Windows-IoT wieder zu einem Objekt dezuserialisieren?
Das ich Klassen nicht "mischen" kann, habe ich schon zähneknirschend in kauf genommen. Aber es muss doch irgendwie möglich sein, auf beiden Systemen die "gleiche Sprache zu sprechen".
Ein Beispiel (was nicht geht aber gerne hätte):
Ich erstelle in meiner Desktop-App eine Klassenbibliothek, in dieser ist u.a. eine Klasse. In dieser Klasse befinden sich drei Eigenschaften. Test1 As String, Test2 As Integer, Test3 As DateTime. Dieses möchte ich serialisieren. Jetzt möchte ich diese XML per TCP auf den Raspberry übertragen. Der Transport tut jetzt hier nichts zur Sache, das Modul habe ich bereits fertig. Jetzt hat meine Windows UWP App auf dem Raspberry diese XML empfangen, und soll diese wieder zu einem Objekt machen, wo Test1 As String drin ist, Test2 As Integer und Test3 As DateTime.Aber ich bekomme es einfach nicht hin. Ich beiße mir seit Wochen daran die Zähne aus.
Ich habe mich erstmal damit beholfen, dass ich die Befehle als "Klartext" übertrage und auf der Gegenseite den String parse. Das ist aber alles total unsauber, widerlich, fehleranfällig und will ich nicht. Ich möchte sauber programmieren und jederzeit alles schön erweitern können.
Aber wie mache ich oben genannte Aufgabe?
Gruß
Andy
Antworten
-
Hallo Andy,
XML und JSON ist ein Plattform unabhängiges Format. Ich kann mir nicht vorstellen das irgendeine Programmiersprachen damit nicht umgehen kann.
Hier auch mal ein ganz einfaches Beispiel. Dafür muss man noch Newtonsoft.Json mit Nuget installieren
Private Sub Test() Dim c = New [MyClass]() c.Test1 = "Test" c.Test2 = 1 c.Test3 = DateTime.Now Dim json As String = Newtonsoft.Json.JsonConvert.SerializeObject(c) 'In json ist jetzt nur Text. Man könnte dies auch einfach in einer Textdatei speichern '{"Test1":"Test1","Test2":1,"Test3":"2017-11-18T00:16:35.6662611+01:00"} 'Diesen Text kann man dann wieder in ein Model wandeln Dim c2 = Newtonsoft.Json.JsonConvert.DeserializeObject(Of [MyClass])(json) End Sub Public Class [MyClass] Public Property Test1() As String Get Return m_Test1 End Get Set m_Test1 = Value End Set End Property Private m_Test1 As String Public Property Test2() As Integer Get Return m_Test2 End Get Set m_Test2 = Value End Set End Property Private m_Test2 As Integer Public Property Test3() As DateTime Get Return m_Test3 End Get Set m_Test3 = Value End Set End Property Private m_Test3 As DateTime End Class
Gruß Thomas
Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!
Cross Platform Canvas for UWP, Android, iOS
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Hi Andreas,
hier mal ein Szenario mit Server-Anwendung als Windows Konsolenanwendung und einer UWP-Anwendung. Der Server empfängt ein Json-Stream über TCP, derserialisiert ihn, erzeugt ein Objekt einer eigenen Klasse, die natürlich zum Json passen muss, verändert das Objekt und schickt das veränderte Objekt per TCP zurück. Die UWP-Anwendung erzeugt ein Objekt vom Typ einer eigenen Klasse, serialisiert dieses Objekt als Json-Stream, sendet den Stream per TCP an den Server, erhält eine Antwort wieder als serialisiertes Json-Datenobjekt und deserialisiert es..XAML UWP:
<Page x:Class="App1.Page04" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:Page04VM x:Key="vm"/> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource vm}"> <Button Content="Abrufen" Command="{Binding Cmd}"/> <TextBlock Text="{Binding Anzeige, Mode=OneWay}"/> </StackPanel> </Page>
ViewModel (UWP):
using Newtonsoft.Json; using System; using System.ComponentModel; using System.IO; using System.Windows.Input; using Windows.Networking; using Windows.Networking.Sockets; using Windows.UI.Xaml.Controls; namespace App1 { public class Page04VM : INotifyPropertyChanged { private int nr = 0; private string hostName = "192.168.0.23"; private string serverPort = "5000"; public string Anzeige { get; set; } = "<leer>"; public ICommand Cmd { get { return new RelayCommand(CmdExec); } } private async void CmdExec(object obj) { try { StreamSocket socket = new StreamSocket(); HostName serverHost = new HostName(hostName); await socket.ConnectAsync(serverHost, serverPort); // Data object zum Senden nr++; Page04Data d1 = new Page04Data() { ID = nr, Info1 = $"Zeile {nr}", Info2 = "für Antwort" }; // Write data to the server. using (Stream outp = socket.OutputStream.AsStreamForWrite()) using (StreamWriter wrt = new StreamWriter(outp)) { await wrt.WriteLineAsync(JsonConvert.SerializeObject(d1)); await wrt.FlushAsync(); } // Data object für Empfang Page04Data d2 = null; //Read data from erver. using (Stream inp = socket.InputStream.AsStreamForRead()) using (StreamReader rdr = new StreamReader(inp)) { string resp = await rdr.ReadLineAsync(); Anzeige = resp; d2 = JsonConvert.DeserializeObject<Page04Data>(resp); Anzeige += $"{Environment.NewLine}Received Object from Server ID:{ d2.ID}, Info1: { d2.Info1}, Info2: { d2.Info2}"; } } catch (Exception ex) { Anzeige = ex.ToString(); } OnPropertyChanged(nameof(Anzeige)); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public class Page04Data { public int ID { get; set; } public string Info1 { get; set; } public string Info2 { get; set; } } }
Server als Konsolenanwendung:
using Newtonsoft.Json; using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program04 { static void Main(string[] args) { try { Server s = new Server(); s.Execute(); Console.WriteLine("Weiter, beenden mit beliebiger Taste"); Console.ReadKey(); s.Stop(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.Write("Fertig, Abschluss mit beliebiger Taste"); Console.ReadKey(); } /// <summary> /// Klasse, die serverseitige Funktionen realisiert /// </summary> internal class Server { private int _listeningPort = 5000; private string _listenningIPAddress = "192.168.0.23"; CancellationTokenSource source = new CancellationTokenSource(); CancellationToken token; public Server() { token = source.Token; } internal Task Execute() { return (new TaskFactory(token)).StartNew(Start); } internal void Stop() { source.Cancel(); } public async void Start() { IPAddress ipAddre = IPAddress.Parse(_listenningIPAddress); TcpListener listener = new TcpListener(ipAddre, _listeningPort); listener.Start(); LogMessage("Server is running"); LogMessage("Listening on port " + _listeningPort); while (true) { LogMessage("Waiting for connections..."); try { var tcpClient = await listener.AcceptTcpClientAsync(); HandleConnectionAsync(tcpClient); } catch (Exception exp) { LogMessage(exp.ToString()); } } } /// <summary> /// mit aufgebauter Verbindung auf Sockets warten /// </summary> /// <param name="tcpClient">client connections for TCP network services</param> private void HandleConnectionAsync(TcpClient tcpClient) { tcpClient.LingerState.Enabled = false; string clientInfo = tcpClient.Client.RemoteEndPoint.ToString(); LogMessage(string.Format("Got connection request from {0}", clientInfo)); try { Data d = null; using (NetworkStream str = tcpClient.GetStream()) { using (StreamWriter wrt = new StreamWriter(str)) { str.ReadTimeout = 600; using (StreamReader rdr = new StreamReader(str)) { string req = rdr.ReadLine(); LogMessage($"Receive from Client:{req}"); d = GetData<Data>(req); LogMessage($"Received Object from Client ID:{d.ID}, Info1:{d.Info1}, Info2:{d.Info2}"); d.Info2 = $"Antwort vom Server für ID {d.ID}"; string resp = GetJSon<Data>(d); wrt.WriteLine(resp); wrt.Flush(); } } } } catch (Exception exp) { LogMessage(exp.ToString()); } finally { LogMessage(string.Format("Closing the client connection - {0}", clientInfo)); tcpClient.Close(); } } internal void LogMessage(string msg) => Console.WriteLine("Server: " + msg); private T GetData<T>(string json) { var obj = JsonConvert.DeserializeObject<T>(json); return obj; } private string GetJSon<T>(T obj) { var res = JsonConvert.SerializeObject(obj); return res; } } public class Data { public int ID { get; set; } public string Info1 { get; set; } public string Info2 { get; set; } } } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Samstag, 18. November 2017 10:14
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
- Bearbeitet Peter Fleischer Samstag, 18. November 2017 15:06
-
Hi Andreas,
hier dasselbe nochmals nicht mit Json, sondern mit XML. Dazu ist auszutauschen:ViewModel:
... private async void CmdExec(object obj) { try { StreamSocket socket = new StreamSocket(); HostName serverHost = new HostName(hostName); await socket.ConnectAsync(serverHost, serverPort); // Data object zum Senden nr++; Page05Data d1 = new Page05Data() { ID = nr, Info1 = $"Zeile {nr}", Info2 = "für Antwort" }; // Write data to the server. using (Stream outp = socket.OutputStream.AsStreamForWrite()) using (StreamWriter wrt = new StreamWriter(outp)) { await wrt.WriteLineAsync(GetXML<Page05Data>(d1)); await wrt.FlushAsync(); } // Data object für Empfang Page05Data d2 = null; //Read data from erver. using (Stream inp = socket.InputStream.AsStreamForRead()) using (StreamReader rdr = new StreamReader(inp)) { string resp = await rdr.ReadLineAsync(); Anzeige = resp; d2 = GetData<Page05Data>(resp); Anzeige += $"{Environment.NewLine}Received Object from Server ID:{ d2.ID}, Info1: { d2.Info1}, Info2: { d2.Info2}"; } } catch (Exception ex) { Anzeige = ex.ToString(); } OnPropertyChanged(nameof(Anzeige)); } private string GetXML<T>(T obj) { XmlSerializer ser = new XmlSerializer(obj.GetType()); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, obj); string res = Encoding.UTF8.GetString(ms.ToArray()); res = res.Replace(Environment.NewLine, ""); return res; } private T GetData<T>(string xml) { MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); XmlSerializer ser = new XmlSerializer(typeof(T)); T res = (T)ser.Deserialize(ms); return res; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } [XmlRoot("Root")] public class Page05Data { [XmlElement("ID")] public int ID { get; set; } [XmlElement("Info1")] public string Info1 { get; set; } [XmlElement("Info2")] public string Info2 { get; set; } }
Und in der Konsolenanwendung:
... /// <summary> /// mit aufgebauter Verbindung auf Sockets warten /// </summary> /// <param name="tcpClient">client connections for TCP network services</param> private async void HandleConnectionAsync(TcpClient tcpClient) { tcpClient.LingerState.Enabled = false; string clientInfo = tcpClient.Client.RemoteEndPoint.ToString(); LogMessage(string.Format("Got connection request from {0}", clientInfo)); try { Data d = null; using (NetworkStream str = tcpClient.GetStream()) { using (StreamWriter wrt = new StreamWriter(str)) { str.ReadTimeout = 600; using (StreamReader rdr = new StreamReader(str)) { string req = await rdr.ReadLineAsync(); LogMessage($"Receive from Client:{req}"); d = GetData<Data>(req); LogMessage($"Received Object from Client ID:{d.ID}, Info1:{d.Info1}, Info2:{d.Info2}"); d.Info2 = $"Antwort vom Server für ID {d.ID}"; string resp = GetXML<Data>(d); await wrt.WriteLineAsync(resp); await wrt.FlushAsync(); } } } } catch (Exception exp) { LogMessage(exp.ToString()); } finally { LogMessage(string.Format("Closing the client connection - {0}", clientInfo)); tcpClient.Close(); } } internal void LogMessage(string msg) => Console.WriteLine("Server: " + msg); private T GetData<T>(string xml) { MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); XmlSerializer ser = new XmlSerializer(typeof(T)); T res = (T)ser.Deserialize(ms); return res; } private string GetXML<T>(T obj) { XmlSerializer ser = new XmlSerializer(typeof(T)); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, obj); string res = Encoding.UTF8.GetString(ms.ToArray()); res = res.Replace(Environment.NewLine, ""); return res; } } [XmlRoot("Root")] public class Data { [XmlElement("ID")] public int ID { get; set; } [XmlElement("Info1")] public string Info1 { get; set; } [XmlElement("Info2")] public string Info2 { get; set; } } } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Samstag, 18. November 2017 10:14
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Also ganz verstehen kann ich dein Problem noch nicht. Der Transfer der Dateien stellt kein Problem dar, schreibst du. Lediglich das de/serialisieren.
Eigentlich musst du doch nur das NuGet-Paket "Netonsoft.Jason" jeweils in deine Projekte installieren und dann kannst du mit den Methoden, die ich in meinem ersten Post hab, deine Klasse de/serialisieren. Du musst lediglich noch dein Speicherort festlegen, das sieht dann bei UWP und Klassischer Anwendung ein wenig anders aus.
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Ich habe mal kurz etwas zusammengezimmert...
KLASSE, welche zu de/serialiesieren ist, sie ist in beiden Projekten vorhanden:
using System; namespace App3 { public class PlayerModel { public string FullName { get; set; } public string ShortName { get; set; } public string ImageName { get; set; } public DateTime Timestamp { get; set; } public string Description { get; set; } } }
UWP - Jason:
using Newtonsoft.Json; using System; using System.Threading.Tasks; using Windows.Storage; namespace App3 { public static class Json { public static async Task<T> ToObjectAsync<T>(string value) { return await Task.Run<T>(() => { return JsonConvert.DeserializeObject<T>(value); }); } public static async Task<string> StringifyAsync(object value) { return await Task.Run<string>(() => { return JsonConvert.SerializeObject(value); }); } } public static class SettingsStorageExtensions { private const string fileExtension = ".json"; public static async Task SaveAsync<T>(this StorageFolder folder, string name, T content) { var file = await folder.CreateFileAsync(GetFileName(name), CreationCollisionOption.ReplaceExisting); var fileContent = await Json.StringifyAsync(content); await FileIO.WriteTextAsync(file, fileContent); } private static string GetFileName(string name) { return string.Concat(name, fileExtension); } } }
UWP - Aufruf:
private void Button_Click(object sender, RoutedEventArgs e) { PlayerModel playerModel = new PlayerModel(); playerModel.FullName = "Fullname1"; playerModel.ShortName = "Shortname1"; playerModel.ImageName = "Image1"; playerModel.Description = "Des1"; playerModel.Timestamp = DateTime.Now; SettingsStorageExtensions.SaveAsync(ApplicationData.Current.LocalFolder, "savejson", playerModel); }
WPF C# - Jason:
using Newtonsoft.Json; using System.IO; using System.Threading.Tasks; namespace WpfApp1 { public static class Json { public static async Task<T> ToObjectAsync<T>(string value) { return await Task.Run<T>(() => { return JsonConvert.DeserializeObject<T>(value); }); } public static async Task<string> StringifyAsync(object value) { return await Task.Run<string>(() => { return JsonConvert.SerializeObject(value); }); } } public static class SettingsStorageExtensions { private const string fileSettings = "savejson.json"; public static async Task SaveAsync<T>(T content) { var fileContent = await Json.StringifyAsync(content); // Set a variable to the My Documents path. string mydocpath = fileSettings; // Write the text asynchronously to a new file named "WriteTextAsync.txt". using (StreamWriter outputFile = new StreamWriter(mydocpath)) { await outputFile.WriteAsync(fileContent); } } } }
WPF C# - Aufruf:
private async void Button_Click(object sender, RoutedEventArgs e) { PlayerModel playerModel = new PlayerModel(); playerModel.FullName = "Fullname1"; playerModel.ShortName = "Shortname1"; playerModel.ImageName = "Image1"; playerModel.Description = "Des1"; playerModel.Timestamp = DateTime.Now; await SettingsStorageExtensions.SaveAsync<PlayerModel>(playerModel); }
Wenn ich das so teste, bekomme ich aus beiden Anwendungen die gleiche Dateistruktur, welche sich problem gegenseitig lesen/schreiben lässt.
Gruß, Stefan
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
Alle Antworten
-
Hallo, ich nehme an, das deine Desktop-App eine UWP ist. Dann ist das de/serialisieren doch eigentlich kein Problem.
Ich habe vor kurzem etwas ähnliches gemacht. Allerdings mit JSON, was ich dir auch empfehlen würde, da die Dateigröße wesentlich kleiner ist und meine Kommunikation läuft per Bluetooth, wobei ich dann keine Datei schreibe, sondern lediglich mit einem stream arbeite.
Letztendlich brauchst du nur in beiden Projekten die gleiche Klasse. Wenn deine Desktop-App eine UWP ist, würde ich eine Projektmappe mit beiden Apps anlegen und zusätzlich ein weiteres Projekt, welches deine Daten-Klasse enthält, das du in beiden App-Projekten referenzierst.
Das de/serialisieren kann dann relativ einfach aussehen (json):
{ public static async Task<T> ToObjectAsync<T>(string value) { return await Task.Run<T>(() => { return JsonConvert.DeserializeObject<T>(value); }); } public static async Task<string> StringifyAsync(object value) { return await Task.Run<string>(() => { return JsonConvert.SerializeObject(value); }); } }
Aber zeig uns doch mal, was du schon hast.
Gruß, Stefan
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
-
Hallo Stefan,
leider nein. Mit Desktop-App meine ich eine Klassische Desktop-Anwendung. Sorry, dass ich mich da jetzt falsch ausgedrückt habe :-/
Ich möchte gerne ein Objekt von meiner Klassischen Windows Anwendung zu einer UWP-App als XML (oder auch JSON) schicken und wieder zurück.
Zwischen UWP und UWP wäre ja kein Problem. Genauso wie zwischen Klassischem und Klassischem.
Gruß
Andy -
Hi Andreas,
Deine Schilderungen lassen vermuten, dass Du unpassend an die Sache herangehst.1. Es wird ein Objekt vom Typ einer Klasse erzeugt.
2. Das Objekt wird serialisiert und es wird eine XML-Bytefolge oder JSon-Bytefolge erzeugt.
3. Die Bytefolge wird an die andere Anwendung geschickt.
4. Die andere Anwendung deserialisiert die Bytefolge in ein Objekt von einem Typ. Den Typ kennt die andere Anwendung, so dass sich der Strom auch deserialisieren lässt.Dieser Ablauf funktioniert zwischen den unterschiedlichsten Systemen, auch zwischen Desktop-App und UWP-App.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Bearbeitet Peter Fleischer Freitag, 17. November 2017 21:26
-
Hi Andreas,
Deine Schilderungen lassen vermuten, dass Du unpassend an die Sache herangehst....
4. Die andere Anwendung deserialisiert die Bytefolge in ein Objekt von einem Typ. Den Typ kennt die andere Anwendung, so dass sich der Strom auch deserialisieren lässt.
Dieser Ablauf funktioniert zwischen den unterschiedlichsten Systemen, auch zwischen Desktop-App und UWP-App.
Hallo Peter,
okay... kannst du mir auch bitte ein Beispiel zeigen? Ich habe es bisher nicht hinbekommen und ich stoße immer wieder auf Beiträge, wo es angeblich nicht gehen soll bzw. kann. Das ist ja auch meine Frage ganz am Anfang. Ich habe es bisher auch nicht hinbekommen und bekam Fehlermeldungen ohne Ende. Dann hab ich es mit UniversalSerializer ausprobiert, die versprach, genau dieses Problem zu beheben. Aber diese stieg gleich beim instanziieren auf Windows IoT aus.
Gruß
Andy -
Also ganz verstehen kann ich dein Problem noch nicht. Der Transfer der Dateien stellt kein Problem dar, schreibst du. Lediglich das de/serialisieren.
Eigentlich musst du doch nur das NuGet-Paket "Netonsoft.Jason" jeweils in deine Projekte installieren und dann kannst du mit den Methoden, die ich in meinem ersten Post hab, deine Klasse de/serialisieren. Du musst lediglich noch dein Speicherort festlegen, das sieht dann bei UWP und Klassischer Anwendung ein wenig anders aus.
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Ich habe mal kurz etwas zusammengezimmert...
KLASSE, welche zu de/serialiesieren ist, sie ist in beiden Projekten vorhanden:
using System; namespace App3 { public class PlayerModel { public string FullName { get; set; } public string ShortName { get; set; } public string ImageName { get; set; } public DateTime Timestamp { get; set; } public string Description { get; set; } } }
UWP - Jason:
using Newtonsoft.Json; using System; using System.Threading.Tasks; using Windows.Storage; namespace App3 { public static class Json { public static async Task<T> ToObjectAsync<T>(string value) { return await Task.Run<T>(() => { return JsonConvert.DeserializeObject<T>(value); }); } public static async Task<string> StringifyAsync(object value) { return await Task.Run<string>(() => { return JsonConvert.SerializeObject(value); }); } } public static class SettingsStorageExtensions { private const string fileExtension = ".json"; public static async Task SaveAsync<T>(this StorageFolder folder, string name, T content) { var file = await folder.CreateFileAsync(GetFileName(name), CreationCollisionOption.ReplaceExisting); var fileContent = await Json.StringifyAsync(content); await FileIO.WriteTextAsync(file, fileContent); } private static string GetFileName(string name) { return string.Concat(name, fileExtension); } } }
UWP - Aufruf:
private void Button_Click(object sender, RoutedEventArgs e) { PlayerModel playerModel = new PlayerModel(); playerModel.FullName = "Fullname1"; playerModel.ShortName = "Shortname1"; playerModel.ImageName = "Image1"; playerModel.Description = "Des1"; playerModel.Timestamp = DateTime.Now; SettingsStorageExtensions.SaveAsync(ApplicationData.Current.LocalFolder, "savejson", playerModel); }
WPF C# - Jason:
using Newtonsoft.Json; using System.IO; using System.Threading.Tasks; namespace WpfApp1 { public static class Json { public static async Task<T> ToObjectAsync<T>(string value) { return await Task.Run<T>(() => { return JsonConvert.DeserializeObject<T>(value); }); } public static async Task<string> StringifyAsync(object value) { return await Task.Run<string>(() => { return JsonConvert.SerializeObject(value); }); } } public static class SettingsStorageExtensions { private const string fileSettings = "savejson.json"; public static async Task SaveAsync<T>(T content) { var fileContent = await Json.StringifyAsync(content); // Set a variable to the My Documents path. string mydocpath = fileSettings; // Write the text asynchronously to a new file named "WriteTextAsync.txt". using (StreamWriter outputFile = new StreamWriter(mydocpath)) { await outputFile.WriteAsync(fileContent); } } } }
WPF C# - Aufruf:
private async void Button_Click(object sender, RoutedEventArgs e) { PlayerModel playerModel = new PlayerModel(); playerModel.FullName = "Fullname1"; playerModel.ShortName = "Shortname1"; playerModel.ImageName = "Image1"; playerModel.Description = "Des1"; playerModel.Timestamp = DateTime.Now; await SettingsStorageExtensions.SaveAsync<PlayerModel>(playerModel); }
Wenn ich das so teste, bekomme ich aus beiden Anwendungen die gleiche Dateistruktur, welche sich problem gegenseitig lesen/schreiben lässt.
Gruß, Stefan
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Hallo Andy,
XML und JSON ist ein Plattform unabhängiges Format. Ich kann mir nicht vorstellen das irgendeine Programmiersprachen damit nicht umgehen kann.
Hier auch mal ein ganz einfaches Beispiel. Dafür muss man noch Newtonsoft.Json mit Nuget installieren
Private Sub Test() Dim c = New [MyClass]() c.Test1 = "Test" c.Test2 = 1 c.Test3 = DateTime.Now Dim json As String = Newtonsoft.Json.JsonConvert.SerializeObject(c) 'In json ist jetzt nur Text. Man könnte dies auch einfach in einer Textdatei speichern '{"Test1":"Test1","Test2":1,"Test3":"2017-11-18T00:16:35.6662611+01:00"} 'Diesen Text kann man dann wieder in ein Model wandeln Dim c2 = Newtonsoft.Json.JsonConvert.DeserializeObject(Of [MyClass])(json) End Sub Public Class [MyClass] Public Property Test1() As String Get Return m_Test1 End Get Set m_Test1 = Value End Set End Property Private m_Test1 As String Public Property Test2() As Integer Get Return m_Test2 End Get Set m_Test2 = Value End Set End Property Private m_Test2 As Integer Public Property Test3() As DateTime Get Return m_Test3 End Get Set m_Test3 = Value End Set End Property Private m_Test3 As DateTime End Class
Gruß Thomas
Sage nie, ich kann es nicht - sage nur, ich kann es noch nicht!
Cross Platform Canvas for UWP, Android, iOS
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Hi Andreas,
hier mal ein Szenario mit Server-Anwendung als Windows Konsolenanwendung und einer UWP-Anwendung. Der Server empfängt ein Json-Stream über TCP, derserialisiert ihn, erzeugt ein Objekt einer eigenen Klasse, die natürlich zum Json passen muss, verändert das Objekt und schickt das veränderte Objekt per TCP zurück. Die UWP-Anwendung erzeugt ein Objekt vom Typ einer eigenen Klasse, serialisiert dieses Objekt als Json-Stream, sendet den Stream per TCP an den Server, erhält eine Antwort wieder als serialisiertes Json-Datenobjekt und deserialisiert es..XAML UWP:
<Page x:Class="App1.Page04" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Page.Resources> <local:Page04VM x:Key="vm"/> </Page.Resources> <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" DataContext="{StaticResource vm}"> <Button Content="Abrufen" Command="{Binding Cmd}"/> <TextBlock Text="{Binding Anzeige, Mode=OneWay}"/> </StackPanel> </Page>
ViewModel (UWP):
using Newtonsoft.Json; using System; using System.ComponentModel; using System.IO; using System.Windows.Input; using Windows.Networking; using Windows.Networking.Sockets; using Windows.UI.Xaml.Controls; namespace App1 { public class Page04VM : INotifyPropertyChanged { private int nr = 0; private string hostName = "192.168.0.23"; private string serverPort = "5000"; public string Anzeige { get; set; } = "<leer>"; public ICommand Cmd { get { return new RelayCommand(CmdExec); } } private async void CmdExec(object obj) { try { StreamSocket socket = new StreamSocket(); HostName serverHost = new HostName(hostName); await socket.ConnectAsync(serverHost, serverPort); // Data object zum Senden nr++; Page04Data d1 = new Page04Data() { ID = nr, Info1 = $"Zeile {nr}", Info2 = "für Antwort" }; // Write data to the server. using (Stream outp = socket.OutputStream.AsStreamForWrite()) using (StreamWriter wrt = new StreamWriter(outp)) { await wrt.WriteLineAsync(JsonConvert.SerializeObject(d1)); await wrt.FlushAsync(); } // Data object für Empfang Page04Data d2 = null; //Read data from erver. using (Stream inp = socket.InputStream.AsStreamForRead()) using (StreamReader rdr = new StreamReader(inp)) { string resp = await rdr.ReadLineAsync(); Anzeige = resp; d2 = JsonConvert.DeserializeObject<Page04Data>(resp); Anzeige += $"{Environment.NewLine}Received Object from Server ID:{ d2.ID}, Info1: { d2.Info1}, Info2: { d2.Info2}"; } } catch (Exception ex) { Anzeige = ex.ToString(); } OnPropertyChanged(nameof(Anzeige)); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } public class Page04Data { public int ID { get; set; } public string Info1 { get; set; } public string Info2 { get; set; } } }
Server als Konsolenanwendung:
using Newtonsoft.Json; using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program04 { static void Main(string[] args) { try { Server s = new Server(); s.Execute(); Console.WriteLine("Weiter, beenden mit beliebiger Taste"); Console.ReadKey(); s.Stop(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.Write("Fertig, Abschluss mit beliebiger Taste"); Console.ReadKey(); } /// <summary> /// Klasse, die serverseitige Funktionen realisiert /// </summary> internal class Server { private int _listeningPort = 5000; private string _listenningIPAddress = "192.168.0.23"; CancellationTokenSource source = new CancellationTokenSource(); CancellationToken token; public Server() { token = source.Token; } internal Task Execute() { return (new TaskFactory(token)).StartNew(Start); } internal void Stop() { source.Cancel(); } public async void Start() { IPAddress ipAddre = IPAddress.Parse(_listenningIPAddress); TcpListener listener = new TcpListener(ipAddre, _listeningPort); listener.Start(); LogMessage("Server is running"); LogMessage("Listening on port " + _listeningPort); while (true) { LogMessage("Waiting for connections..."); try { var tcpClient = await listener.AcceptTcpClientAsync(); HandleConnectionAsync(tcpClient); } catch (Exception exp) { LogMessage(exp.ToString()); } } } /// <summary> /// mit aufgebauter Verbindung auf Sockets warten /// </summary> /// <param name="tcpClient">client connections for TCP network services</param> private void HandleConnectionAsync(TcpClient tcpClient) { tcpClient.LingerState.Enabled = false; string clientInfo = tcpClient.Client.RemoteEndPoint.ToString(); LogMessage(string.Format("Got connection request from {0}", clientInfo)); try { Data d = null; using (NetworkStream str = tcpClient.GetStream()) { using (StreamWriter wrt = new StreamWriter(str)) { str.ReadTimeout = 600; using (StreamReader rdr = new StreamReader(str)) { string req = rdr.ReadLine(); LogMessage($"Receive from Client:{req}"); d = GetData<Data>(req); LogMessage($"Received Object from Client ID:{d.ID}, Info1:{d.Info1}, Info2:{d.Info2}"); d.Info2 = $"Antwort vom Server für ID {d.ID}"; string resp = GetJSon<Data>(d); wrt.WriteLine(resp); wrt.Flush(); } } } } catch (Exception exp) { LogMessage(exp.ToString()); } finally { LogMessage(string.Format("Closing the client connection - {0}", clientInfo)); tcpClient.Close(); } } internal void LogMessage(string msg) => Console.WriteLine("Server: " + msg); private T GetData<T>(string json) { var obj = JsonConvert.DeserializeObject<T>(json); return obj; } private string GetJSon<T>(T obj) { var res = JsonConvert.SerializeObject(obj); return res; } } public class Data { public int ID { get; set; } public string Info1 { get; set; } public string Info2 { get; set; } } } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Samstag, 18. November 2017 10:14
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
- Bearbeitet Peter Fleischer Samstag, 18. November 2017 15:06
-
Hi Andreas,
hier dasselbe nochmals nicht mit Json, sondern mit XML. Dazu ist auszutauschen:ViewModel:
... private async void CmdExec(object obj) { try { StreamSocket socket = new StreamSocket(); HostName serverHost = new HostName(hostName); await socket.ConnectAsync(serverHost, serverPort); // Data object zum Senden nr++; Page05Data d1 = new Page05Data() { ID = nr, Info1 = $"Zeile {nr}", Info2 = "für Antwort" }; // Write data to the server. using (Stream outp = socket.OutputStream.AsStreamForWrite()) using (StreamWriter wrt = new StreamWriter(outp)) { await wrt.WriteLineAsync(GetXML<Page05Data>(d1)); await wrt.FlushAsync(); } // Data object für Empfang Page05Data d2 = null; //Read data from erver. using (Stream inp = socket.InputStream.AsStreamForRead()) using (StreamReader rdr = new StreamReader(inp)) { string resp = await rdr.ReadLineAsync(); Anzeige = resp; d2 = GetData<Page05Data>(resp); Anzeige += $"{Environment.NewLine}Received Object from Server ID:{ d2.ID}, Info1: { d2.Info1}, Info2: { d2.Info2}"; } } catch (Exception ex) { Anzeige = ex.ToString(); } OnPropertyChanged(nameof(Anzeige)); } private string GetXML<T>(T obj) { XmlSerializer ser = new XmlSerializer(obj.GetType()); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, obj); string res = Encoding.UTF8.GetString(ms.ToArray()); res = res.Replace(Environment.NewLine, ""); return res; } private T GetData<T>(string xml) { MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); XmlSerializer ser = new XmlSerializer(typeof(T)); T res = (T)ser.Deserialize(ms); return res; } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } [XmlRoot("Root")] public class Page05Data { [XmlElement("ID")] public int ID { get; set; } [XmlElement("Info1")] public string Info1 { get; set; } [XmlElement("Info2")] public string Info2 { get; set; } }
Und in der Konsolenanwendung:
... /// <summary> /// mit aufgebauter Verbindung auf Sockets warten /// </summary> /// <param name="tcpClient">client connections for TCP network services</param> private async void HandleConnectionAsync(TcpClient tcpClient) { tcpClient.LingerState.Enabled = false; string clientInfo = tcpClient.Client.RemoteEndPoint.ToString(); LogMessage(string.Format("Got connection request from {0}", clientInfo)); try { Data d = null; using (NetworkStream str = tcpClient.GetStream()) { using (StreamWriter wrt = new StreamWriter(str)) { str.ReadTimeout = 600; using (StreamReader rdr = new StreamReader(str)) { string req = await rdr.ReadLineAsync(); LogMessage($"Receive from Client:{req}"); d = GetData<Data>(req); LogMessage($"Received Object from Client ID:{d.ID}, Info1:{d.Info1}, Info2:{d.Info2}"); d.Info2 = $"Antwort vom Server für ID {d.ID}"; string resp = GetXML<Data>(d); await wrt.WriteLineAsync(resp); await wrt.FlushAsync(); } } } } catch (Exception exp) { LogMessage(exp.ToString()); } finally { LogMessage(string.Format("Closing the client connection - {0}", clientInfo)); tcpClient.Close(); } } internal void LogMessage(string msg) => Console.WriteLine("Server: " + msg); private T GetData<T>(string xml) { MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); XmlSerializer ser = new XmlSerializer(typeof(T)); T res = (T)ser.Deserialize(ms); return res; } private string GetXML<T>(T obj) { XmlSerializer ser = new XmlSerializer(typeof(T)); MemoryStream ms = new MemoryStream(); ser.Serialize(ms, obj); string res = Encoding.UTF8.GetString(ms.ToArray()); res = res.Replace(Environment.NewLine, ""); return res; } } [XmlRoot("Root")] public class Data { [XmlElement("ID")] public int ID { get; set; } [XmlElement("Info1")] public string Info1 { get; set; } [XmlElement("Info2")] public string Info2 { get; set; } } } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Samstag, 18. November 2017 10:14
- Als Antwort markiert AndreasMahub Samstag, 18. November 2017 10:43
-
Guten Morgen Peter,
Guten Morgen Stefan,
Guten Morgen Thomas,ich möchte euch allen drei danken. Ihr habt euch sehr viel Mühe gegeben und es funktioniert! Sogar mit meiner Verschlüsselung (wird nicht als Klartext übertragen alles, sondern am anderen Ende erst wieder entschlüsselt und dann mit Newtonsoft.Json behandelt).
Ich nehme das jetzt auch so wie ihr mir das vorgeschlagen habt. Nur wollte ich EIGENTLICH (Großbuchstaben nicht als schreien verstehen) mit BORDMITTELN das lösen. Ich wollte nach Möglichkeit OHNE NuGet-Paketen mit DLLs von Drittanbietern arbeiten.
Ja, mag ja sein das Newtonsoft.JSON das absolute Non-plus-Ultra ist. Und natürlich sind eine klassische Desktop Anwendung und Windows IoT für unterschiedliche Prozessor-Architekturen. Aber manchmal kommt mir das alles auch so "unfertig" vor und man muss sich dann mit Drittanbietern behelfen.
Egal, es funktioniert erstmal und gefällt mir 1000x besser.
DANKE an euch!
Gruß
Andy -
Hi Andy,
ohne Json-Nuget geht das mit Bordmitteln per XML. Nur leider ist da die übertragene Datenmenge um Einiges größer.--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks