LINQ to XML API allows us to create complete XML document as expected with all the elements. So this X-DOM has everything as you expect.
Simple sample looks like,
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-16", "true"),
new XProcessingInstruction("test", "value"),
new XComment("This is comment by you"),
new XElement("Employees",
new XElement("Employee",
new XAttribute("id", "EMP001"),
new XElement("name", "Wriju"),
new XCData("~~~~~~~XML CDATA~~~~~~~~"))));
By calling Save method of XDocument (actually of XContainer) you can save it to a physical file. And the file will look like,
<?xml version="1.0" encoding="utf-16" ?>
<?test value?>
<!-- This is comment by you -->
<Employees>
<Employee id="EMP001">
<name>Wriju</name>
<![CDATA[ ~~~~~~~XML CDATA~~~~~~~~]]>
</Employee>
</Employees>
To me this looks like more aligned to the human thinking mechanism.
Namoskar!!!
The Bill Gates Stanford Speech
Are you a student? Want to develop using Microsoft product? We are giving it for free. Know more
Bill Gates talks about Free Software, Students, and Technology
Based on my previous post if I have to filter the list of customers only in the country “US” and create another XML which may look like,
<?xml version="1.0" encoding="utf-8"?>
<usaCustomers>
<usaCustomer>
<title>Marketing Manager</title>
<name>Howard Snyder</name>
</usaCustomer>
<title>Sales Representative</title>
<name>Yoshi Latimer</name>
<name>John Steel</name>
….
To get this if I have to use the System.Xml namespace then code will become something like,
//Conventional XML way to generate another XML out of this XML
XmlDocument customers = new XmlDocument();
customers.Load(@"C:\XMLData.xml");
XmlDocument usaCustomers = new XmlDocument();
XmlElement usaRoot = usaCustomers.CreateElement("usaCustomers");
usaCustomers.AppendChild(usaRoot);
foreach (XmlNode n in customers.SelectNodes(
"/customers/customer[@country='USA']"))
{
XmlElement usaCustomer = usaCustomers.CreateElement("usaCustomer");
XmlElement usaTitle = usaCustomers.CreateElement("title");
usaTitle.InnerText = n.Attributes["contactTitle"].Value;
usaCustomer.AppendChild(usaTitle);
XmlElement usaContact = usaCustomers.CreateElement("name");
usaContact.InnerText = n.Attributes["contactName"].Value;
usaCustomer.AppendChild(usaContact);
usaRoot.AppendChild(usaCustomer);
}
This approach works perfectly fine but for this you also need to learn XPath and XQuery on top of conventional System.Xml.
To achieve this in simple “what you think what you write” scenario you can go for LINQ to XML.
//LINQ to XML way
XElement root = new XElement("usaCustomers",
from c in XElement.Load(@"C:\XMLData.xml").Descendants("customer")
where (string)c.Attribute("country") == "USA"
select new XElement("usaCustomer",
new XElement("title", (string)c.Attribute("contactTitle")),
new XElement("name", (string)c.Attribute("contactName"))));
Its simple its magic. Thanks to Mike Taulty for my learning.
We can generate hierarchical object graph in our memory though LINQ. To be more realistic we can bring data from relational database. So if we consider Northwind database and use LINQ to SQL to bring all the Customers and their Orders and Order Details the query would look like,
//LINQ to SQL way to get data from database
var q = from c in db.Customers
select new
CId = c.CustomerID,
Orders = from o in c.Orders
OID = o.OrderID,
Qty = from od in o.Order_Details
select new { Qty = od.Quantity }
};
So what I am trying to do here is that, I am trying to fetch CustomerId from Customers table and OrderId from Orders table and Quantity from Order Details table. It is bringing 3 level deep data for me and storing it to memory.
By using XElement and XAttribute I will create a single XML stream. Which will look like,
<customers>
<customer id="ALFKI" country="Germany" contactName="Maria Anders" contactTitle="Sales Representative">
<Orders id="10643" date="1997-08-25T00:00:00">
<items>
<item price="45.6000" quantity="15" />
<item price="18.0000" quantity="21" />
<item price="12.0000" quantity="2" />
</items>
</Orders>
<Orders id="10692" date="1997-10-03T00:00:00">
<item price="43.9000" quantity="20" />
…….
To achieve this I have to write a very simple query like syntax based on the query I have written earlier,
var query = new XElement("customers",
from c in db.Customers
select
new XElement("customer",
new XAttribute("id", c.CustomerID),
new XAttribute("country", c.Country),
new XAttribute("contactName", c.ContactName),
new XAttribute("contactTitle", c.ContactTitle),
from o in c.Orders
select new XElement("Orders",
new XAttribute("id", o.OrderID),
new XAttribute("date", o.OrderDate),
new XElement("items",
from od in o.Order_Details
select new XElement("item",
new XAttribute("price", od.UnitPrice),
new XAttribute("quantity", od.Quantity))))));
It looks complex because it is one liner but actually it is very simple. This will give you the exact XML output mentioned earlier.
XElement and XAttribute are the two very important classes available in System.Xml.Linq.dll assembly. Using these two classes you can do lot of things in the LINQ to XML world. I will show you step by step how,
For just an element
XElement _root = new XElement("root");
_root.Save(fileName);
And the output will look like,
<?xml version="1.0" encoding="utf-8" ?>
<root />
Now if you want to add child to the root,
XElement _child = new XElement("child");
_root.Add(_child);
The generated XML would look like,
<root>
<child />
</root>
This work like DOM.
Now the .Add() method allows you pass values in form of object. So we can pass anything there,
XAttribute attr = new XAttribute("attrbt", 2008);
_root.Add(attr);
The XML view will be like,
<root attrbt="2008">
Now another interesting part is that the Add() method not only takes an object it also accepts array of objects. So you ideally do not have to call .Add() multiple times.
With the same output as before you can create you code,
_root.Add(_child, attr);
For more child you can simply keep on adding elements to the Add() method separated by comma.
XElement _child1 = new XElement("child1");
XElement _child2 = new XElement("child2");
XElement _child3 = new XElement("child3");
_root.Add(_child1, _child2, _child3, attr);
Now the output goes,
<child1 />
<child2 />
<child3 />
By using List<T> you can elegantly add elements to the root. Same output for this code,
List<XElement> childs = new List<XElement>
new XElement("child1"),
new XElement("child2"),
new XElement("child3")
_root.Add(childs, attr);
Now with all these options you can go ahead and use the constructor to add elements and attribute to your root.
So if I want to generate previous XML in one liner way, I could do this,
XElement _root = new XElement("root",
new List<XElement>
},
new XAttribute("attrbt", 2008));
//_root.Add(childs, attr);
Now I do not require Add() method at all.
Hope this gives you the idea about the basics of XElement and XAttribute. Thanks to Mike Taulty , I have learnt this from one of his finest demos.
I have discussed about the native XML support in VB.NET code editor in one of my BLOG posts. Now to generate a XML dynamically with values you can play with LINQ and project it to a XML.
Let us get the list of process running into my local machine with their thread count,
Original LINQ
Dim query = From p In System.Diagnostics.Process.GetProcesses() _
Select New With _
{ _
.ProcessName = p.ProcessName, _
.ThreadCount = p.Threads.Count _
Structure of XML I want to create
<Processes>
<Process ThreadCount="2">Some Name</Process>
</Processes>
To generate XML you may have this code,
Dim _xml3 = _
<%= From p In System.Diagnostics.Process.GetProcesses() _
Select <Process ThreadCount=<%= p.Threads.Count %>><%= p.ProcessName %></Process> %>
Console.WriteLine(_xml)
Dim fileName As String = "C:\MyProcesses.xml"
_xml3.Save(fileName)
Shell("notepad " + fileName, AppWinStyle.NormalFocus)
This will also save the XML in a file and you will find the XML header.
This is the one feature which can make VB.NET developers feel better than C# folks. I have seen people complaining about MS saying that MS has done lot for C# but not equally for VB.NET. One of the biggest was Refractoring. There are so many such. But this feature is just “SPELLBOUND” when I first experienced it.
Today, people handle XML from .NET in many ways. One approach is they simply concatenate the string to create XML string and end up writing to a file. Another is they generate DataSet/DataTable and uses WriteXml() method to write Xml to a file. More sophisticated way is to use System.Xml namespace and build Xml step by step in strongly typed manner. But this one is something which is simply amazing.
In Visual Studio 2008 if you write something using System.Xml.Linq
Dim _xml2 = New XElement("ProcessList", _
New XElement("Process", _
New XAttribute("ThreadCount", "2"), "Some Process"))
You can also write,
Dim _xml = <Processes>
These two statements are identical. Second one looks like an ASP.NET page but it is actually XElement object. But the feeling you will get as if you are writing an Xml in a XML Editor.
Coolest ever.
Today I found an article with very detailed discussion http://geekswithblogs.net/Silverlight2/archive/2008/03/30/xml-literals-and-embedded-expressions.aspx
If you want to use Lambda Expression as reusable function, you need to use Func. By using the Func we are actually calling in-build delegate.
So if you write
Dim sqr As Func(Of Double, Double) = Function(x) x * x
Then you can use it in you application to get the square of a number.
Console.WriteLine(sqr(2))
Console.WriteLine(sqr(8))
This could be used in multiple places with different parameter as you generally do with any function.
In VB.NET 9.0 Lambda is one of the features we have here. Lambda expression is just another way to call Anonymous method/delegate.
Let’s look into a generic list of integers, and play with it,
Dim arrInt As New List(Of Integer)
For i As Integer = 1 To 10
arrInt.Add(i)
Next
When you need to get the even numbers out of this List, you can call delegate,
Dim even1 As New List(Of Integer)
even1 = arrInt.FindAll(New Predicate(Of Integer)(AddressOf EvenGetter))
Then for this approach you need a method,
Public Function EvenGetter(ByVal i2 As Integer) As Boolean
Return i2 Mod 2 = 0
End Function
Using VB.NET 9.0 you can also implement Lambda Expression,
even1 = arrInt.FindAll(Function(i2 As Integer) i2 Mod 2 = 0)
Ask expert for more.
Last week I was exploring VB.NET for the ISV demo delivery. Some findings I want to share with you. First the object and Array Initializers
Let’s suppose we have a class called Customer
Public Class Customer
Public Id As Integer
Public Name As String
End Class
Now when you initialize this object in conventional VB.NET, this could be your approach,
Dim cust As New Customer()
With cust
.Id = 1
.Name = "VB.NET"
End With
Now in VB.NET 9.0 we do things in little differently,
Dim cust = New Customer() With {.Id = 2, .Name = "VB.NET 9.0"}
Also for array initialization in VB.NET we go for,
Dim objCusts(1) As Customer
objCusts(0) = New Customer() With {.Id = 3, .Name = "VB.NET 10.0"}
objCusts(1) = New Customer() With {.Id = 4, .Name = "VB.NET 11.0"}
In VB.NET 9.0 we write,
Dim objCusts() As Customer = { _
New Customer() With {.Id = 3, .Name = "VB.NET 10.0"}, _
New Customer() With {.Id = 4, .Name = "VB.NET 11.0"}}
It is small (to me simple), it is sweet.