Category Archives: XML
Remoting (part 2, advanced), WCF
WCF improves code concepts substantially. There are templates like “WCF Service Application” in Visual Studio making programming extremely easy. You get results with a few clicks.
I am concentrating on an independent code solution today. This basically is a similar solution to my remoting example in part 1. You can clearly see the differences rather than starting an entirely new project type.
We need two App.config files.
One on the client side
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint name="ServerInstance" address="net.tcp://localhost/ServerInstance" binding="netTcpBinding" bindingConfiguration="tcp_Unsecured" contract="Shared.MyInterface"/> </client> <bindings> <netTcpBinding> <binding name="tcp_Unsecured"> <security mode="None" /> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
and one on the server-side.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="Server.ServerInstance"> <endpoint address="net.tcp://localhost/ServerInstance" binding="netTcpBinding" bindingConfiguration="tcp_Unsecured" contract="Shared.MyInterface" /> </service> </services> <bindings> <netTcpBinding> <binding name="tcp_Unsecured"> <security mode="None" /> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
I guess you have already noticed the Shared.MyInterface. This is the same approach as in the classical Remoting. We share data definitions.
using System; using System.Collections.Generic; using System.ServiceModel; namespace Shared { [ServiceContract(SessionMode = SessionMode.Allowed)] public interface MyInterface { [OperationContract] List<TradeData> GetTrades(); [OperationContract] string HelloWorld(string xName); [OperationContract] DateTime Ping(); } // interface } // namespace
The shared area also needs our TradeData class, which is used inside the Interface. Do not forget the Serializable attribute. You do not necessarily get an error message. The program can simply refuse proper execution and only provide mind-boggling error messages.
using System; namespace Shared { [Serializable] public class TradeData { public DateTime tradeTime; public string ticker; public double price; public int quantity; public override string ToString() { return tradeTime.ToString("dd.MMM.yy HH:mm:ss ") + ticker + " " + quantity + " @ " + price + " EUR"; } // public TradeData(DateTime xTradeTime, string xTicker, double xPrice, int xQuantity) { tradeTime = xTradeTime; ticker = xTicker; price = xPrice; quantity = xQuantity; } // constructor } // class } // namespace
The class ServerInstance is part of the server-side. It inherits MyInterface and adds the required statements of MyInterface.
using System; using System.Collections.Generic; using System.ServiceModel; using Shared; namespace Server { [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] internal class ServerInstance : MyInterface { public List<TradeData> GetTrades() { DateTime lDummyTime = new DateTime(2014, 02, 12, 16, 30, 10); TradeData lTrade1 = new TradeData(lDummyTime, "DTE", 11.83, 100); TradeData lTrade2 = new TradeData(lDummyTime.AddSeconds(2), "DAI", 66.45, 300); TradeData lTrade3 = new TradeData(lDummyTime.AddSeconds(5), "DBK", 35.91, 100); return new List<TradeData>() { lTrade1, lTrade2, lTrade3 }; } // public string HelloWorld(string xName) { return "Hello " + xName; } // public DateTime Ping() { return DateTime.Now; } // } // class } // namespace
The server side code is relatively short. It is more or less a two liner to instantiate a ServiceHost and open it.
using System; using System.ServiceModel; using System.Threading; namespace Server { public class Program { public static void Main(string[] arguments) { Thread.Sleep(10000); // we let the client wait 10 seconds to simulate a server that is down using (ServiceHost lServiceHost = new ServiceHost(typeof(ServerInstance))) { lServiceHost.Open(); Console.WriteLine("Service is available."); Console.ReadKey(); } } // main } // class } // namespace
And the client side is not much longer. Have a look for yourself. Most of the lines are taking care of printing the data to the console window.
using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.Threading; using Shared; namespace Client { public class Program { public static void Main(string[] args) { ChannelFactory<MyInterface> lFactory = new ChannelFactory<MyInterface>("ServerInstance"); MyInterface lShared = null; while (true) { try { lShared = lFactory.CreateChannel(); var o = lShared.GetTrades(); var s = lShared.HelloWorld("Mr. Ohta"); var p = lShared.Ping(); Console.WriteLine("GetTrades():"); foreach (var lTrade in o) Console.WriteLine(lTrade.ToString()); Console.WriteLine(); Console.WriteLine("HelloWorld(): " + s); Console.WriteLine(); Console.WriteLine("Ping(): " + p); break; } catch (Exception ex) { Console.WriteLine(ex.Message); Thread.Sleep(2000); } finally { IChannel lChannel = lShared as IChannel; if (lChannel.State == CommunicationState.Opened) lChannel.Close(); } } Console.ReadKey(); } // main } // class } // namespace
After starting the client and server concurrently,
you should get output results like these:
SERVER example output:
Service is available.
CLIENT example output:
Could not connect to net.tcp://localhost/ServerInstance. The connection attempt
lasted for a time span of 00:00:02.0521174. TCP error code 10061: No connection
could be made because the target machine actively refused it 127.0.0.1:808.
Could not connect to net.tcp://localhost/ServerInstance. The connection attempt
lasted for a time span of 00:00:02.0391166. TCP error code 10061: No connection
could be made because the target machine actively refused it 127.0.0.1:808.
GetTrades():
12.Feb.14 16:30:10 DTE 100 @ 11.83 EUR
12.Feb.14 16:30:12 DAI 300 @ 66.45 EUR
12.Feb.14 16:30:15 DBK 100 @ 35.91 EURHelloWorld(): Hello Mr. Ohta
Ping(): 17/02/2014 12:30:59
The error messages on the client side are as expected. They are caused by “Thread.Sleep(10000);” on the server side. I thought it would be interesting to show a server which is not running at the time when starting the client.
Put aside all the complex and confusing examples on the internet. This one here is spot on. It provides a nice and comfortable entry point into WCF. The code does not have to be complex.
I recommend YouTube for further WCF explanations.
JSON (basics), follow-up to XML
XML is quite verbose. This leads to higher memory usage and slowness during the (de-)serialization process. There are alternatives in the market that also support the cross-platform approach. The most basic one, which is used among web APIs, is JSON. It is well-integrated into JavaScript.
The .Net framework supports JSON. Thus JSON is a good start to slowly delve into posts about networking. There are more advanced cross-platform solutions. Jackson, GSON and BSON are some of the really good ones. Besides this you can always use the good old .Net data serialization (or remoting). If you need speed rather than flexibility, then I would probably recommend to use your own binary protocol. Unfortunately custom binary protocols are error-prone, debugging can be hell and the control factor for IT support people is close to zero in case the protocol is corrupt. Does transmitting data take longer than encoding/packing data? That is the fundamental question. Protocol Buffers will most likely be the “binary number cruncher’s” best friend. It is a free (Apache License) cross plattforms product. There is also a specific version for the .Net environment. Protocol Buffers is not the fastest tool in the market, but all faster tools I know are (like kryo) for Java only. Protocol Buffers is supported by many programming languages. Its speed is pretty good.
As usual plenty of posts lie ahead of us.
Back to Json.
Here is a quick example from wiki:
This is a follow-up on XML. So we have to start with an XML example somehow 🙂
<person> <firstName>John</firstName> <lastName>Smith</lastName> <age>25</age> <address> <streetAddress>21 2nd Street</streetAddress> <city>New York</city> <state>NY</state> <postalCode>10021</postalCode> </address> <phoneNumbers> <phoneNumber type="home">212 555-1234</phoneNumber> <phoneNumber type="fax">646 555-4567</phoneNumber> </phoneNumbers> </person>
or
<person firstName="John" lastName="Smith" age="25"> <address streetAddress="21 2nd Street" city="New York" state="NY" postalCode="10021" /> <phoneNumbers> <phoneNumber type="home" number="212 555-1234"/> <phoneNumber type="fax" number="646 555-4567"/> </phoneNumbers> </person>
And JSON looks more like C#, doesn’t it?
Btw. there is a really nice JSON online viewer here.
{ "firstName": "John", "age": 25, "address": { "streetAddress": "21 2nd Street", "city": "New York", "state": "NY", "postalCode": "10021" }, "phoneNumber": [ { "type": "home", "number": "212 555-1234" }, { "type": "fax", "number": "646 555-4567" } ] }
First of all we need to make JavaScriptSerializer available. Go to your Solution Explorer and right-click your project, then select “Properties”. Change your “Target Framework” on the “Application” page to “.Net Framework 4” or higher. Do not select “.Net Framework 4 Client Profile“.
Add the reference “System.Web.Extensions” to your project. The namespace “System.Web.Script.Serialization” is available now.
(In case it is still in your AssemblyInfo.cs, remove [assembly: AllowPartiallyTrustedCallersAttribute])
Let’s now convert our “Walmart.xml” example to JSON. The example shows how to serialize objetcs. Nothing was changed in the class WalmartWorld (see earlier post).
public static void SaveObjectsJSON(WalmartWorld xWalmartWorld) { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; JavaScriptSerializer lSerializer = new JavaScriptSerializer(); string lSerialized = lSerializer.Serialize(xWalmartWorld); File.WriteAllText(lFile, lSerialized); } //
Formatted example output:
{ "Food": [ { "Attribute": null, "Name": "Banana", "Price": 1.99, "Description": "Mexican delicious", "SomethingElse": null }, { "Attribute": null, "Name": "Rice", "Price": 0.79, "Description": "the best you can get", "SomethingElse": null }, { "Attribute": "What a wonderful world.", "Name": "Cornflakes", "Price": 3.85, "Description": "buy some milk", "SomethingElse": null }, { "Attribute": null, "Name": "Milk", "Price": 1.43, "Description": "from happy cows", "SomethingElse": null }, { "Attribute": null, "Name": "baked beans", "Price": 1.35, "Description": "very British", "SomethingElse": null } ], "Electronic": [ { "Attribute": null, "Name": "Kindle fire", "Price": 100, "Description": "Amazon loves you", "SomethingElse": "the perfect Xmas gift for your kids" } ], "Text": "Luis Armstrong had a fabulous voice." }
public static void LoadObjectJSON() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; JavaScriptSerializer lSerializer = new JavaScriptSerializer(); string lSerialized = File.ReadAllText(lFile); WalmartWorld lWalmartWolrd = lSerializer.Deserialize<WalmartWorld>(lSerialized); foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem); foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem); Console.ReadLine(); } //
example output:
Kindle fire 100.00 Amazon loves you !!! => the perfect Xmas gift for your kids Banana 1.99 Mexican delicious Rice 0.79 the best you can get Cornflakes 3.85 buy some milk Milk 1.43 from happy cows baked beans 1.35 very British
Unfortunately we have the same mess as with XML. There are several classes dealing with JSON in the .Net framework.
There is another serializer in the System.Runtime.Serialization.Json namespace called DataContractJsonSerializer. It is intended for use with WCF client applications. It does make use of DataContractAttribute and DataMemberAttribute to identify the data to serialize. If you do not add any of these attributes, then data is ignored.
Add the “System.Runtime.Serialization” reference to your project.
Let’s extend our Walmart classes and add the required attributes. You can remove the XML attributes if you do not want to work with XML anymore. You can leave them in the code to keep the back door open for quick and easy XML conversion. The XML attributes do not affect the program, there are just some extra bytes.
BUT beware of a little typo. If you use DataContractSerializer instead of DataContractJsonSerializer, then you will accidentally create XML and not Json files. Such little problems in life can drive you nuts.
Further reading for DataContractAttribute and DataMemberAttribute.
[Serializable] [DataContract(Name="WalmartWorld")] [XmlRoot("Walmart", Namespace = "")] public class WalmartWorld { [XmlElement("food")] [DataMember] public List<WalmartItem> Food { get; set; } [XmlElement("electronic")] [DataMember] public List<WalmartItem> Electronic { get; set; } [XmlText] [DataMember] public string Text { get; set; } } // class [Serializable] [DataContract(Name = "WalmartItem")] [XmlRoot("Walmart", Namespace = "")] public class WalmartItem { [XmlAttribute("attr")] [DataMember(Name = "Attribute")] // just to demonstrate that you could define a name public string Attribute { get; set; } [XmlElement("name")] [DataMember] public string Name { get; set; } [XmlElement("price")] [DataMember] public double Price { get; set; } [XmlElement("description")] [DataMember] public string Description { get; set; } [XmlElement("somethingElse")] [DataMember] public string SomethingElse { get; set; } public override string ToString() { return Name.PadRight(12) + Price.ToString("#,##0.00").PadLeft(8) + " " + Description + (string.IsNullOrEmpty(SomethingElse) ? string.Empty : (" !!! => " + SomethingElse)); } // } // class public static void SaveObjectsJSON2(WalmartWorld xWalmartWorld) { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart2.json"; using (FileStream lFileStream = new FileStream(lFile, FileMode.OpenOrCreate)) { DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld)); lSerializer.WriteObject(lFileStream, xWalmartWorld); } } // public static WalmartWorld LoadObjectJSON2() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.json"; DataContractJsonSerializer lSerializer = new DataContractJsonSerializer(typeof(WalmartWorld)); using (FileStream lFileStream = new FileStream(lFile, FileMode.Open)) { WalmartWorld lWalmartWolrd = lSerializer.ReadObject(lFileStream) as WalmartWorld; foreach (WalmartItem lItem in lWalmartWolrd.Electronic) Console.WriteLine(lItem); foreach (WalmartItem lItem in lWalmartWolrd.Food) Console.WriteLine(lItem); return lWalmartWolrd; } } //
You could define your custom Name for each DataMember attribute. The JSON file would then address this name instead of the property name.
[DataMember(Name = "UseWhateverYouLike")] public string Attribute { get; set; }
XML (part 4, advanced), XSD verification
Data integrity is a big issue in XML. You can generate XML Schemas to define your data structure and automate data integrity checks. As mentioned before, I am not going to explain the XSD structure itself. My purpose here is to explain the C# part.
Follow the link for a brilliant XSD introduction.
We are still using the embedded “Walmart.xml” file from the previous posts (1, 2, 3).
The easiest way to create an XML Schema is to use Visual Studio 2013. Open the XML file “Walmart.xml”. Then select “Create Schema” in the Window-Menu “XML” (between TEAM and TOOLS). The resulting XSD file looks like this:
<?xml version="1.0" encoding="utf-8"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Walmart"> <xs:complexType> <xs:sequence> <xs:choice maxOccurs="unbounded"> <xs:element maxOccurs="unbounded" name="food"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string" /> <xs:element name="price" type="xs:decimal" /> <xs:element name="description" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="electronic"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string" /> <xs:element name="price" type="xs:unsignedByte" /> <xs:element name="description" type="xs:string" /> <xs:element name="somethingElse" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>
You can play with this file and modify it later on. Btw. one line should be amended from
<xs:element name="price" type="xs:unsignedByte" />
to
<xs:element name="price" type="xs:decimal" />
But for now let’s stick to the automatically generated XSD file as it is. Add the file to your Solution Explorer right next to “Walmart.xml” and set the property “Build Action” of “Walmart.xsd” to “Embedded Resource”.
private static void ValidationEvents(object sender, ValidationEventArgs e) { switch (e.Severity) { case XmlSeverityType.Error: Console.WriteLine("Error: " + e.Message); break; case XmlSeverityType.Warning: Console.WriteLine("Warning: " + e.Message); break; } } // public static void ValidateXML() { Assembly lAssembly = Assembly.GetExecutingAssembly(); using (Stream lStreamXml = lAssembly.GetManifestResourceStream("DemoApp.XmlFiles.Walmart.xml")) { using (Stream lStreamXsd = lAssembly.GetManifestResourceStream("DemoApp.XmlFiles.Walmart.xsd")) { XmlDocument lXmlDocument = new XmlDocument(); XmlSchema x = XmlSchema.Read(lStreamXsd, new ValidationEventHandler(ValidationEvents)); lXmlDocument.Schemas.Add(x); lXmlDocument.Load(lStreamXml); lXmlDocument.Validate(ValidationEvents); } } Console.WriteLine("press enter to exit program"); Console.ReadLine(); } //
Run ValidateXML(). There should be no error message. Now change the XSD or XML file and see what error messages you get.
XML (part 1, basics), file IO
The last days were tough. Writing posts requires a lot of time. Explaining some basic know-how will give me some recreation today.
I will concentrate on the C# parts. There will be no explanations about XML, which is well documented across the internet.
XML stands for eXtensible Markup Language. XML is a versatile and flexible data structure. It is easy to read, learn and understand. The downside is the amount of bytes it needs. You can use XML to transport and/or store data, but it quickly becomes quite inefficient when a lot of datasets are involved.
XML is a well established industry standard. So there is no way around it, you have to know at least something about it.
Here is an XML file that we will use in the loading example:
<?xml version="1.0" encoding="UTF-8"?> <!-- XML EXAMPLE --> <Walmart> <food> <name>Banana</name> <price>1.99</price> <description>Mexican delicious</description> </food> <food> <name>Rice</name> <price>0.79</price> <description>the best you can get</description> </food> <food> <name>Cornflakes</name> <price>3.85</price> <description>buy some milk</description> </food> <food> <name>Milk</name> <price>1.43</price> <description>from happy cows</description> </food> <electronic> <name>Kindle fire</name> <price>100</price> <description>Amazon loves you</description> <somethingElse>the perfect Xmas gift for your kids</somethingElse> </electronic> <food> <name>baked beans</name> <price>1.35</price> <description>very British</description> </food> </Walmart>
Paste the XML structure into a text editor and save it as an XML file (“Walmart.xml”) onto your desktop.
The following class will be used to store datasets in memory.
public class WmItem { public readonly string name; public readonly double price; public readonly string description; public WmItem(string xName, double xPrice, string xDescription) { name = xName; price = xPrice; description = xDescription; } // constructor public override string ToString() { return name.PadRight(12) + price.ToString("#,##0.00").PadLeft(8) + " " + description; } // } // class
Loading XML files is easy. Processing files takes a bit longer. The centerpiece generally is the parsing algorithm.
There is a field called “somethingElse” in the XML file. It does not cause any trouble. The code simply disregards it. I added it to demonstrate the “eXtensible” in “eXtensible Markup Language”.
public static void LoadXml() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Walmart.xml"; XDocument lXDocument = XDocument.Load(lFile); // food (using WmItem) WmItem[] lFood = (from lData in lXDocument.Descendants("Walmart").Descendants("food") select new WmItem( lData.Element("name").Value, double.Parse(lData.Element("price").Value), lData.Element("description").Value) ).ToArray(); foreach (WmItem lItem in lFood) Console.WriteLine(lItem); Console.WriteLine(); // electronic (quick and dirty, using var) var lElectronic = from lData in lXDocument.Descendants("Walmart").Descendants("electronic") select lData; foreach (var lItem in lElectronic) { Console.WriteLine(lItem); Console.WriteLine(); Console.WriteLine(lItem.Element("name").Value); Console.WriteLine(lItem.Element("price").Value); Console.WriteLine(lItem.Element("description").Value); } Console.ReadLine(); } //
example output:
Banana 1.99 Mexican delicious
Rice 0.79 the best you can get
Cornflakes 3.85 buy some milk
Milk 1.43 from happy cows
baked beans 1.35 very British<electronic>
<name>Kindle fire</name>
<price>100</price>
<description>Amazon loves you</description>
<somethingElse>the perfect Xmas gift for your kids</somethingElse>
</electronic>Kindle fire
100
Amazon loves you
Saving XML is also straight forward. I added a comment and attributes for demonstration purposes. The program generates and saves the XML file “Genesis.xml” on your desktop.
public static void SaveXml() { string lDesktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + @"\"; string lFile = lDesktopPath + "Genesis.xml"; WmItem[] lMetals = { new WmItem("Lead", 1.0, "here we go"), new WmItem("Silver", 2.0, "cutlery"), new WmItem("Gold", 3.0, "wife's best friend"), new WmItem("Platinum", 4.0, "posh") }; XDocument lXDocument = new XDocument(); lXDocument.Declaration = new XDeclaration("1.0", "utf-8", "yes"); lXDocument.Add(new XComment("copyfight by Bastian M.K. Ohta")); XElement lLME = new XElement("London_Metal_Exchange", new XAttribute("attribute1", "buy here"), new XAttribute("AreYouSure", "yes")); lXDocument.Add(lLME); foreach (WmItem lMetal in lMetals) { XElement lGroup = new XElement("metal"); lGroup.Add(new XElement("name", lMetal.name)); lGroup.Add(new XElement("price", lMetal.price)); lGroup.Add(new XElement("description", lMetal.description)); lLME.Add(lGroup); } lXDocument.Save(lFile); //Console.ReadLine(); } //