generating KML file using C# and LINQ with a recursive function - c#

I am trying to create a KML file dynamically in C#. I have wrote a recursive function to do this. However the result of output has a bit problem. The problem is the position of closing tags of all placemarks. I am really confused. Please tell me where am I making mistake in the recursive function???
My code:
private void xmlBuild()
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", ""),
new XComment("This is comment by me"),
new XElement(ns+"kml",
new XElement(ns+"Document", rec_build())));
doc.Save(Server.MapPath(#"~\App_Data\markers2.xml"));
}
private XElement rec_build()
{
if (iteration != 0)
{
iteration -= 1;
return final_rec = new XElement(ns + "Placemark",
new XAttribute("id", "1"),
new XElement(ns + "title", "something"),
new XElement(ns + "description", "something"),
new XElement(ns + "LookAt",
new XElement(ns + "Longitude", "49.69"),
new XElement(ns + "Latitude", "32.345")), new XElement(ns + "Point", new XElement(ns + "coordinates", "49.69,32.345,0")),rec_build());
}
else
{
return null;
}
}
and this is the output for iteration value of 2: (please notice the closing tags of placemark id=1 at the end of file. It should be before the starting tag of placemark id=2!
<?xml version="1.0" encoding="utf-8"?>
<!--This is comment by me-->
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
<Placemark id="1">
<title>something</title>
<description>something</description>
<LookAt>
<Longitude>49.69</Longitude>
<Latitude>32.345</Latitude>
</LookAt>
<Point>
<coordinates>49.69,32.345,0</coordinates>
</Point>
<Placemark id="1">
<title>something</title>
<description>something</description>
<LookAt>
<Longitude>49.69</Longitude>
<Latitude>32.345</Latitude>
</LookAt>
<Point>
<coordinates>49.69,32.345,0</coordinates>
</Point>
</Placemark>
</Placemark>
</Document>
</kml>

So the problem is each time you recurse, you are adding the element to the newly created item. It seems that a loop would work better.
Essentially code is doing this:
set up the kml outbody
1st call and add element (element 1) to kml outerboy
2nd call add element (element 2) to (element 1)
3rd call add element (element 3) to (element 2).
If you wanted to do a recursive method rather then the looping mechanism, pass in a reference to the outer kml .
The recursive is more confusing if this is exactly how it works
(Sorry if I have an extra or missing parenthesis, comma, or other item. I don't have VS installed on this)
Looping:
private void xmlBuild()
{
XElement documentElement = new XElement(ns + "Document");
for (int i = 0; i < 2; i++)
{
documentElement.Add(rec_build());
}
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", ""),
new XComment("This is comment by me"),
new XElement(ns + "kml", documentElement));
doc.Save(Server.MapPath(#"~\App_Data\markers2.xml"));
}
private XElement rec_build()
{
return new XElement(ns + "Placemark",
new XAttribute("id", "1"),
new XElement(ns + "title", "something"),
new XElement(ns + "description", "something"),
new XElement(ns + "LookAt",
new XElement(ns + "Longitude", "49.69"),
new XElement(ns + "Latitude", "32.345")),
new XElement(ns + "Point",
new XElement(ns + "coordinates", "49.69,32.345,0")));
}
Recursive:
private void xmlBuild()
{
XElement docElement = new XElement(ns+"Document");
rec_build(docElement);
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", ""),
new XComment("This is comment by me"),
new XElement(ns+"kml", docElement)));
doc.Save(Server.MapPath(#"~\App_Data\markers2.xml"));
}
private XElement rec_build(XElement doc)
{
if (iteration != 0)
{
iteration -= 1;
doc.Add(new XElement(ns + "Placemark",
new XAttribute("id", "1"),
new XElement(ns + "title", "something"),
new XElement(ns + "description", "something"),
new XElement(ns + "LookAt",
new XElement(ns + "Longitude", "49.69"),
new XElement(ns + "Latitude", "32.345")),
new XElement(ns + "Point", new XElement(ns + "coordinates", "49.69,32.345,0")));
return recBuild(doc);
}
else
{
return null;
}
}

You are adding the recursively build elements as children of Placemark and not Document. This should do the trick:
private void xmlBuild()
{
XElement docElement = new XElement(ns + "Document");
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", ""),
new XComment("This is comment by me"),
new XElement(ns + "kml", docElement));
rec_build(docElement);
doc.Save(Server.MapPath(#"~\App_Data\markers2.xml"));
}
private XElement rec_build(XElement docElement)
{
if (iteration != 0)
{
iteration -= 1;
return final_rec = new XElement(ns + "Placemark",
new XAttribute("id", "1"),
new XElement(ns + "title", "something"),
new XElement(ns + "description", "something"),
new XElement(ns + "LookAt",
new XElement(ns + "Longitude", "49.69"),
new XElement(ns + "Latitude", "32.345")),
new XElement(ns + "Point", new XElement(ns + "coordinates", "49.69,32.345,0")));
docElement.Add(final_rec);
rec_build(docElement);
}
else
return null;
}

Related

Error parsing xml syntax error

I have the following code below. I am getting an error "the character ':' hexadecimal value 0x3A cannot be included in a name" Can anyone tell me how to fix this?
Thanks Below is the entire code
public static XDocument GenerateXMLSpreadSheet(DataTable tbl)
{
new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XProcessing**Instruction("mso-application", "Excel.Sheet"),
new XElement("Workbook",
new XAttribute("xmlns", "urn:schemas-microsoft-com:office:spreadsheet"),
new XAttribute("xmlns:ss", "urn:schemas-microsoft-
com:office:spreadsheet"),
new XElement("Worksheet", new XAttribute("ss:Name",
tbl.TableName),
new XElement("Table", GetRows(tbl)
)
)
)
);
return xmlssDoc;
)
public static Object[] GetRows(DataTable tbl)
{
// generate XElement rows for each row in the database.
// generate from the bottom-up.
// start with the cells.
XElement[] rows = new XElement[tbl.Rows.Count];
int r = 0;
foreach (DataRow row in tbl.Rows)
{
// create the array of cells to add to the row:
XElement[] cells = new XElement[tbl.Columns.Count];
int c = 0;
foreach (DataColumn col in tbl.Columns)
{
cells[c++] =
new XElement("Cell",
new XElement("Data", new XAttribute("ss:Type", "String"),
new XText(row[col].ToString())));
}
rows[r++] = new XElement("Row", cells);
}
// return the array of rows.
return rows;
}
Basically, that's not how you handle namespaces in LINQ to XML. You never specify a string with a colon in - instead, you build up an XName from an XNamespace and a string.
The good news is that LINQ to XML handling of namespaces is simple. Here's a complete example:
using System;
using System.Xml.Linq;
public class Test
{
static void Main(string[] args)
{
XNamespace ns = "urn:schemas-microsoft-com:office:spreadsheet";
var doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XProcessingInstruction("mso-application", "Excel.Sheet"),
new XElement(ns + "Workbook",
new XAttribute("xmlns", ns),
new XAttribute(XNamespace.Xmlns + "ss", ns),
new XElement(ns + "Worksheet",
new XAttribute(ns + "Name", "my-table-name"),
new XElement(ns + "Table")
)
)
);
Console.WriteLine(doc);
}
}
Output (reformatted for clarity):
<?mso-application Excel.Sheet?>
<ss:Workbook
xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<ss:Worksheet ss:Name="my-table-name">
<ss:Table />
</ss:Worksheet>
</ss:Workbook>
Note how you need to specify the namespace for all the elements and attributes.
Because you've specified an explicit prefix for the namespace, LINQ to XML uses it everywhere. If you remove this part:
new XAttribute(XNamespace.Xmlns + "ss", ns)
... then the namespace for elements will be defaulted, and LINQ to XML will generate a prefix for the attributes that need it explicitly specified (as attributes don't default their namespaces).
Then the output is (for example):
<?mso-application Excel.Sheet?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet">
<Worksheet p2:Name="my-table-name" xmlns:p2="urn:schemas-microsoft-com:office:spreadsheet">
<Table />
</Worksheet>
</Workbook>
The problem is how you defined namespaces.
To work with namespaces you may get one by calling XNamespace.Get("the namespace") and using this everywhere you want.
And for xmlns you may use static property XNamespace.Xmlns.
So the final code will look like:
new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XProcessingInstruction("mso-application", "Excel.Sheet"),
new XElement("Workbook",
new XAttribute("xmlns", ns),
new XAttribute(XNamespace.Xmlns + "ss", ns),
new XElement("Worksheet", new XAttribute(ns + "Name",
tbl.TableName),
new XElement("Table", GetRows(tbl)
)
)
)
);

How to add xmlns to xml document?

I'm trying to create a fairly simple XML document that looks like this.
<?xml version="1.0" encoding="UTF-8"?>
<employees xmlns="http://website.com/xsd/MQ">
<employee>
<id>00122731</id>
<first-name>LUIS</first-name>
<last-name>GARCIA</last-name>
<subarea>4100</subarea>
<cost-center>904</cost-center>
<email-address>L.GARCIA#EMAIL.COM</email-address>
</employee>
</employees>
I was able to get the basics by using this code but I need to add the xmlns attribute and I'm not figuring out how to do that.
var xmlDoc = new XElement("employees",
from e in listEmployees
select new XElement("employee",
new XElement("id", e.EmployeeId),
new XElement("first-name", e.FirstName),
new XElement("last-name", e.LastName),
new XElement("subarea", e.SubArea),
new XElement("cost-center", e.CostCenter),
new XElement("email-address", e.EmailAddress)));
This is my attempt to add it but I get an error that this would cause and invalid structure.
XDocument xmlDoc = new XDocument(
new XElement("employees",
new XAttribute("xmlns", "http://website/xsd/MQ")),
from e in listEmployees
select new XElement("employee",
new XElement("id", e.EmployeeId),
new XElement("first-name", e.FirstName),
new XElement("last-name", e.LastName),
new XElement("subarea", e.SubArea),
new XElement("cost-center", e.CostCenter),
new XElement("email-address", e.EmailAddress))
);
UPDATE
Based on the link provided below this is what I came up with that worked.
XNamespace ns = "http://website/xsd/MQ";
var xmlDoc = new XElement(ns + "employees",
from e in listEmployees
select new XElement("employee",
new XElement("id", e.EmployeeId),
new XElement("first-name", e.FirstName),
new XElement("last-name", e.LastName),
new XElement("subarea", e.SubArea),
new XElement("cost-center", e.CostCenter),
new XElement("email-address", e.EmailAddress)));
You need to include the namespace on all of your elements, not just the top one:
XNamespace ns = "http://website/xsd/MQ";
var xmlDoc = new XElement(ns + "employees",
from e in listEmployees
select new XElement(ns + "employee",
new XElement(ns + "id", e.EmployeeId),
new XElement(ns + "first-name", e.FirstName),
new XElement(ns + "last-name", e.LastName),
new XElement(ns + "subarea", e.SubArea),
new XElement(ns + "cost-center", e.CostCenter),
new XElement(ns + "email-address", e.EmailAddress)
)
);
If that's too repetitive for you, you could make a convenience method:
XNamespace ns = "http://website/xsd/MQ";
private static XElement MQElement(string name, object contents)
{
return new XElement(ns + name, contents);
}
then use it:
var xmlDoc = MQElement("employees",
from e in listEmployees
select MQElement("employee",
MQElement("id", e.EmployeeId),
MQElement("first-name", e.FirstName),
MQElement("last-name", e.LastName),
MQElement("subarea", e.SubArea),
MQElement("cost-center", e.CostCenter),
MQElement("email-address", e.EmailAddress)
)
);
One more option to achieve same result is to construct XElement ignoring namespaces completely and then add them in separate code:
foreach (XElement e in xmlDoc.DescendantsAndSelf())
{
if (e.Name.Namespace == "")
{
e.Name = ns + e.Name.LocalName;
}
}

C# XML - Why does this Code keep failing with 0x3A Error?

i have following code example:
PlentySoapRequest_GetAuthentificationToken username = new PlentySoapRequest_GetAuthentificationToken();
username.Username = user_textbox.ToString();
username.Userpass = password_textbox.ToString();
Uri uri = new Uri("http://www.****.de/plenty/api/soap/version105/");
XNamespace soapenv = #"http://schemas.xmlsoap.org/soap/envelope/";
XNamespace xsi = #"http://www.w3.org/2001/XMLSchema-instance";
XNamespace xsd = #"http://www.w3.org/2001/XMLSchema";
XNamespace ver = #"http://www.****.de/plenty/api/soap/version105/";
var document = new XDocument(
new XDeclaration("1.0", "utf-8", String.Empty),
new XElement(soapenv + "Envelope",
new XAttribute(XNamespace.Xmlns + "xsi", xsi),
new XAttribute(XNamespace.Xmlns + "xsd" , xsd),
new XAttribute(XNamespace.Xmlns + "soapenv" , soapenv),
new XAttribute(XNamespace.Xmlns + "ver" , ver),
new XElement(soapenv + "Header"),
new XElement(soapenv + "Body",
new XElement(ver + "GetAuthentificationToken",
new XElement("oLogin" + xsi + "type" + ver + "PlentySoapRequest_GetAuthentificationToken",
new XAttribute("Username" + xsi + "type" + xsd + "string", username.Username),
new XAttribute("Userpass" + xsi + "type" + xsd + "string", username.Userpass)
)
)
)
)
);
I keep getting the error message in the first line. "plentysoaprequest...."
The ':' character, hexadecimal value 0x3A, cannot be included in a name.
if I comment out those lines, it keeps say it is in the first line of the code.
Edit:
the xml should look like this:
<soapenv:Body>
<ver:GetAuthentificationToken>
<oLogin xsi:type="ver:PlentySoapRequest_GetAuthentificationToken">
<!--You may enter the following 2 items in any order-->
<Username xsi:type="xsd:string">apitest</Username>
<Userpass xsi:type="xsd:string">apitest</Userpass>
</oLogin>
</ver:GetAuthentificationToken>
so there seems to be a problem with the xml-sysntax.
I cant figure out how to set xsi:type or xsi:type
"oLogin" + xsi + "type" will create a string with value "oLoginhttp://www.w3.org/2001/XMLSchema-instancetype". That's not a valid name...
You need something a little closer to this:
var document = new XDocument(
new XDeclaration("1.0", "utf-8", String.Empty),
new XElement(soapenv + "Envelope",
new XAttribute(XNamespace.Xmlns + "xsi", xsi),
new XAttribute(XNamespace.Xmlns + "xsd", xsd),
new XAttribute(XNamespace.Xmlns + "soapenv", soapenv),
new XAttribute(XNamespace.Xmlns + "ver", ver),
new XElement(soapenv + "Header"),
new XElement(soapenv + "Body",
new XElement(ver + "GetAuthentificationToken",
new XElement(xsi + "Login",
new XAttribute(xsi + "type", "blahblah"),
new XElement("Username",
new XAttribute(xsi + "type", "xsd:string"),
"myUserName")
)
)
)
)
);
Which generates this XML
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ver="http://www.****.de/plenty/api/soap/version105/">
<soapenv:Header />
<soapenv:Body>
<ver:GetAuthentificationToken>
<xsi:Login xsi:type="blahblah">
<Username xsi:type="xsd:string">myUserName</Username>
</xsi:Login>
</ver:GetAuthentificationToken>
</soapenv:Body>
</soapenv:Envelope>

How to create an Xml with multiple namescapes using XDocument

I would like to create a document with multiple namespaces (see Xml below). How can I do that using XDocument? My final result should the following:
<?xml version="1.0" encoding="utf-8"?>
<a:feed xmlns:a="http://www.w3.org/2005/Atom" xmlns:os="http://a9.com/-/spec/opensearch/1.1/" xmlns="http://schemas.zune.net/catalog/apps/2008/02">
<a:link rel="prev" type="application/atom+xml" href="myUrl" />
<a:link rel="next" type="application/atom+xml" href="myUrl" />
<a:link rel="self" type="application/atom+xml" href="myUrl" />
<a:updated>2008-05-20T22:50:46.7932864Z</a:updated>
Here is the code I have so far.
XNamespace nsW3Atom = "http://www.w3.org/2005/Atom";
XNamespace nsOs = "http://a9.com/-/spec/opensearch/1.1/";
XNamespace nsZune = "http://schemas.zune.net/calatog/apps/2008/02";
XDocument doc =
new XDocument(
// new XDeclaration("1.0", "utf-8", "no"),
new XElement("feed",
new XAttribute("a", nsW3Atom),
new XAttribute("os", nsOs),
new XAttribute("os2", nsZune),
new XElement(XNamespace.Xmlns + "link", new XAttribute("rel", "prev"), new XAttribute("type", "application/atom+xml")),
new XElement(XNamespace.Xmlns + "link", new XAttribute("rel", "next"), new XAttribute("type", "application/atom+xml")),
new XElement(XNamespace.Xmlns + "link", new XAttribute("rel", "self"), new XAttribute("type", "application/atom+xml"))));
Thank you in advance for the help!
XNamespace a="http://www.w3.org/2005/Atom";
XNamespace os = "http://a9.com/-/spec/opensearch/1.1/";
XNamespace def = "http://schemas.zune.net/catalog/apps/2008/02";
var doc =
new XDocument(new XElement(a + "feed",
new XAttribute(XNamespace.Xmlns + "a", a),
new XAttribute(XNamespace.Xmlns + "os", os),
new XAttribute("xmlns", def)));
then if You want to use the a Namespace, prefix any element with a+
When creating the attributes use the xmlns: prefix.
new XAttribute("xmlns:a", nsW3Atom),
new XAttribute("xmlns:os", nsOs),
new XAttribute("xmlns:os2", nsZune)

How to generate xsi:schemalocation attribute correctly when generating a dynamic sitemap.xml with LINQ to XML?

I am generating a dynamic sitemap.xml
According to sitemaps.org this is the header for a sitemap.xml
<?xml version='1.0' encoding='UTF-8'?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
...
</url>
</urlset>
So I'm using LINQ To XML to generate the sitemap.xml
XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
return new XElement(ns + "urlset",
new XAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
//new XAttribute("xsi:schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"),
from node in new GetNodes()
select new XElement(ns + "url",
new XElement(ns + "loc", node.Loc),
new XElement(ns + "lastmod", node.LastMod),
new XElement(ns + "priority", node.Priority)
)
).ToString();
The commented line is the one i cannot get right.
How can i set the "xsi:schemalocation" attribute?
Thanks.
Ok, I got it right. Thanks to Mike Caron
If I declare the XAtrribute(XNamespace.Xmlns + "xsi",...) then it works
XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
return new XElement(ns + "urlset",
new XAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9"),
new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"),
new XAttribute(xsi + "schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"),
from node in GetNodes()
select new XElement(ns + "url",
new XElement(ns + "loc", node.Loc),
new XElement(ns + "lastmod", node.LastMod),
new XElement(ns + "priority", node.Priority)
)
).ToString();
I don't know LINQ to XML, but after a quick peek at the documentation, try this:
XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
return new XElement(ns + "urlset",
new XAttribute(xsi + "schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"),
from node in new GetNodes()
select new XElement(ns + "url",
new XElement(ns + "loc", node.Loc),
new XElement(ns + "lastmod", node.LastMod),
new XElement(ns + "priority", node.Priority)
)
).ToString();
Note that I'm not setting the xmlns attributes explicitly. I suspect they're generated automatically. Also, caveat emptor, since this is not tested.

Categories