How to use VB Linq features in C# [duplicate] - c#

Is it possible to add literal XML data within a C# code file? I'm currently using a multiline string literal but it gets messy as you can see. Any better way of doing this?
string XML = #"<?xml version=""1.0"" encoding=""utf-8""?>
<customUI xmlns=""http://schemas.example.com/customui"">
<toolbar id=""save"">
</toolbar>
</customUI>";

XML literals are a feature of VB.NET, not C#.
What you have posted is as close as you can get in C#.
You may want to consider replacing the embedded double quotes with single quotes though (as both types are valid XML).
For larger amounts of XML you may want to consider the answer from Marc - using an XML file (loaded once and stored in memory), so you can take advantage of the XML editor.

If the XML is big enough to get in the way, consider using a flat .xml file instead, either loaded from disk, or embedded as a resource. As long as you only load it once (perhaps in a static constructor) this will make no difference to performance. It will be considerably easier to maintain, as it will use the IDE's XML file editor. And it won't get in the way of your code.

With reference to my comment, I couldn't recall where I saw this, but I finally found the XmlBuilder link.
In retrospect, it seems Linq to XML would be your best bet. It's cleaner, faster and more maintainable than concatenating XML strings:
XNamespace ns = "http://schemas.example.com/customui";
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement(ns + "customUI",
new XElement(ns + "taskbar",
new XAttribute("id", "save"))
)
);
var stringWriter = new StringWriter();
doc.Save(stringWriter); //Write to StringWriter, preserving the declaration (<?xml version="1.0" encoding="utf-16" standalone="yes"?>)
var xmlString = stringWriter.ToString(); //Save as string
doc.Save(#"d:\out.xml"); //Save to file

As a peculiar, and very case-specific solution, if you happen to be working in an ASP.NET environment using the Razor engine, in a CSHTML file you can:
Func<MyType, HelperResult> xml = #<root>
<item>#(item.PropertyA)</item>
<item>#(item.PropertyB)</item>
<item>#(item.PropertyC)</item>
</root>;
With the addition of an extension method:
public static XDocument ToXDocument<T>(this Func<T, HelperResult> source, T item)
{
return XDocument.Parse(source(item).ToHtmlString());
}
You can then:
XDocument document = xml.ToXDocument(new MyType() {
PropertyA = "foo",
PropertyB = "bar",
PropertyC = "qux",
});
Again, peculiar? Yes. Case-specific? Yes. But it works, and gives great Intellisense. (mind you, it also will give a bunch of validity warnings, depending on the document validation version)

The closest we could have in C# would be through LINQ, something like that:
var xml = XDocument.Load(
new StringReader(#"<Books>
<Book author='Dan Brown'>The Da Vinci Code</Book>
<Book author='Dan Brown'>The Lost Symbol</Book>
</Books>"));
var query = from book in xml.Elements("Books").Elements("Book")
where book.Attribute("author").Value == "Dan Brown"
select book.Value;
foreach (var item in query) Console.WriteLine(item);

Related

Reading a single node from XML file and using it as a condition

I am simply trying to read a particular node from an XML and use it as a string variable in a condition. This gets me to the XML file and gives me the whole thing.
string url = #"http://agent.mtconnect.org/current";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(url);
richTextBox1.Text = xmlDoc.InnerXml;
But I need the power state "ON" of "OFF" (XML section below, can view the whole XML online)
<Events><PowerState dataItemId="p2" timestamp="2013-03-11T12:27:30.275747" name="power" sequence="4042868976">ON</PowerState></Events>
I have tried everything I know of. I am just not that familiar with XML files. and the other posts get me nowhere.
HELP PLEASE!
You may try LINQ2XML for that:
string value = (string) (XElement.Load("http://agent.mtconnect.org/current")
.Descendants().FirstOrDefault(d => d.Name.LocalName == "PowerState"))
If you wanted to avoid LINQ, or if it is not working for you you can use straight XML traversal for this:
string url = #"http://agent.mtconnect.org/current";
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.Load(url);
System.Xml.XmlNamespaceManager theNameManager = new System.Xml.XmlNamespaceManager(xmlDoc.NameTable);
theNameManager.AddNamespace("mtS", "urn:mtconnect.org:MTConnectStreams:1.2");
theNameManager.AddNamespace("m", "urn:mtconnect.org:MTConnectStreams:1.2");
theNameManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
System.Xml.XmlElement DeviceStreams = (System.Xml.XmlElement)xmlDoc.SelectSingleNode("descendant::mtS:DeviceStream", theNameManager);
System.Xml.XmlNodeList theStreams = DeviceStreams.SelectNodes("descendant::mtS:ComponentStream", theNameManager);
foreach (System.Xml.XmlNode CompStream in theStreams)
{
if (CompStream.Attributes["component"].Value == "Electric")
{
System.Xml.XmlElement EventElement = (System.Xml.XmlElement)CompStream.SelectSingleNode("descendant::mtS:Events", theNameManager);
System.Xml.XmlElement PowerElement = (System.Xml.XmlElement)EventElement.SelectSingleNode("descendant::mtS:PowerState", theNameManager);
Console.Out.WriteLine(PowerElement.InnerText);
Console.In.Read();
}
}
When traversing any document with a default namespace in the root node, I have found it is imperative to have a namespace manager. Without it the document is just un-navigable.
I created this code in a console application. It worked for me. Also I am no guru and I may be making some mistakes here. I am not sure if there is some way to have the default namespace referenced without naming it (mtS). Anyone who knows how to make this cleaner or more efficient please comment.
EDIT:
For one less level of 'clunk' you can change this:
if (CompStream.Attributes["component"].Value == "Electric")
{
Console.Out.WriteLine(((System.Xml.XmlElement)CompStream.SelectSingleNode("descendant::mtS:Events", theNameManager)).InnerText;);
Console.In.Read();
}
because there is only one element in there and its innerText is all you will get.

How to get the value from XML?

I'm making a small tool for checking the material number from the XML file.
I know this quite easy for you experts and I would like to ask for your help on this to get me started on this. On my machine I have .NET 2.0 framework I guess, and VS C# Express 2005 installed.
I have an XML that contains data of a material. It is located at my local drive. I am able now to browse the XML file and save the file in a string variable. Well, that's what I have done so far..
if(folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
string[] files = Directory.GetFiles(folderBrowserDialog1.SelectedPath, "Product.xml");
string prodFile = files[0];
...
...
Suppose this is the structure of the XML:
<Record>
<Product>
<Material_Number>20209485</Material_Number>
<Product_Type>Type1</Product_Type>
...
...
</Product>
</Record>
How can I get the material number value?
You can use the XmlDocument class for loading your XML File into a DOM.
MSDN - This class implements the W3C Document Object Model (DOM) Level 1 Core and the Core DOM Level 2. The DOM is an in-memory (cache) tree representation of an XML document and enables the navigation and editing of this document. Because XmlDocument implements the IXPathNavigable interface it can also be used as the source document for the XslTransform class.
Sample
There are many ways to read your value. I really encourage you to read Working with Xml DOM
XmlNodeList list = xml.GetElementsByTagName("Product");
XmlAttributeCollection attr = list[0].Attributes;
string materialNumber = list[0].ChildNodes[0].InnerText;
or
XmlNodeList list = xml.GetElementsByTagName("Material_Number");
string materialNumber = list[0].InnerText;
More Information
MSDN - XmlDocument Class
Working with Xml DOM
You could also use XPathNavigator and XPathExpression with XmlDocument.
var xmlDoc = new XmlDocument();
xmlDoc.Load("Product.xml") //or xmlDoc.LoadXml(xmlString);
var xmlNav = xmlDoc.CreateNavigator();
string materialNum;
var iterator = xmlNav.Select("/Record/Product/Material_Number");
if (iterator.MoveNext() && iterator.Current != null)
materialNum = iterator.Current.Value;
If you use .Net 3.0+ you could use System.Xml.Linq.XDocument.
var xdoc = XDocument.Load("Product.xml"); //or var xdoc = XDocument.Parse(xmlString);
var materialNum = xdoc.Root.Element("Product").Element("Material_Number").Value;
I was able to find a solution. Not so elegant though...
XmlDocument xmlDoc= new XmlDocument();
xmlDoc.Load(#"C:\Product.xml");
XmlNodeList a = xmlDoc.GetElementsByTagName("Material_Number");
string materialNumber = a[0].InnerText;

Linq To Xml Saving a List Of Nested Objects

I currently load an XML file into a list objects using code like this
XDocument xmlDoc = XDocument.Load(path);
List<ImportDefinition> importDefinitions = xmlDoc.Descendants("Root").Select(xElem => (ImportDefinition)xElem).ToList();
return importDefinitions;
This list of objects contains nested objects and each one has an operator for parsing the XML into the correct form like this
public static explicit operator Rules(XElement xElem)
{
try
{
return new Rules()
{
FileNameRegEx = (string)xElem.Element("FileNameRegEx"),
FileExtension = (string)xElem.Element("FileExtension")
};
}
catch (Exception ex)
{
return null;
}
This works fine for loading the XML. I now want to save this list of objects back to XML after some edits have been made.
I was hoping something like this would work
XElement xml = new XElement("Root",
from p in ObjectList
select new XElement("File",RootObject
));
}
xml.Save("C:\\temp\\newimport.xml");
However this just seems to output this
<?xml version="1.0" encoding="utf-8"?>
<Root>
<File>MyNamespace.RootObject</File>
<File>MyNamespace.RootObject</File>
</Root>
It looks like its not using the custom operators it uses when loading the files to work out the format to save in. Whats the best way to save this data back to XML to the same format it was in when I read it?
Well for one thing you've only shown us the operator for parsing from an XElement... but even so, you're obviously explicitly calling that in your LINQ expression. If you want the equivalent when building XML, you'll need to be explicit there too:
XElement xml = new XElement("Root",
from p in ObjectList
select new XElement("File", (XElement) p));
Personally I'd use methods instead of operators - ToXElement() and FromXElement() - I think it's clearer that way. ToXElement would be an instance method; FromXElement would be a static method. This is a pattern I've used many times, and it's always worked fine.

C# , xml parsing. get data between tags

I have a string :
responsestring = "<?xml version="1.0" encoding="utf-8"?>
<upload><image><name></name><hash>SOmetext</hash>"
How can i get the value between
<hash> and </hash>
?
My attempts :
responseString.Substring(responseString.LastIndexOf("<hash>") + 6, 8); // this sort of works , but won't work in every situation.
also tried messing around with xmlreader , but couldn't find the solution.
ty
Try
XDocument doc = XDocument.Parse(str);
var a = from hash in doc.Descendants("hash")
select hash.Value;
you will need System.Core and System.Xml.Linq assembly references
Others have suggested LINQ to XML solutions, which is what I'd use as well, if possible.
If you're stuck with .NET 2.0, use XmlDocument or even XmlReader.
But don't try to manipulate the raw string yourself using Substring and IndexOf. Use an XML API of some description. Otherwise you will get it wrong. It's a matter of using the right tool for the job. Parsing XML properly is a significant chunk of work - work that's already been done.
Now, just to make this a full answer, here's a short but complete program using your sample data:
using System;
using System.Xml.Linq;
class Test
{
static void Main()
{
string response = #"<?xml version='1.0' encoding='utf-8'?>
<upload><image><name></name><hash>Some text</hash></image></upload>";
XDocument doc = XDocument.Parse(response);
foreach (XElement hashElement in doc.Descendants("hash"))
{
string hashValue = (string) hashElement;
Console.WriteLine(hashValue);
}
}
}
Obviously that will loop over all the hash elements. If you only want one, you could use doc.Descendants("hash").Single() or doc.Descendants("hash").First() depending on your requirements.
Note that both the conversion I've used here and the Value property will return the concatenation of all text nodes within the element. Hopefully that's okay for you - or you could get just the first text node which is a direct child if necessary.
var val = XElement.Parse();
val.Descendants(...).Value
Get your xml well formed and escape the double quotes with backslash. Then apply the following code
XDocument resp = XDocument.Parse("<hash>SOmetext</hash>");
var r= from element in resp.Elements()
where element.Name == "hash"
select element;
foreach (var item in r)
{
Console.WriteLine(item.Value);
}
You can use an xmlreader and/or xpath queries to get all desired data.
XmlReader_Object.ReadToFollowing("hash");
string value = XmlReader_Object.ReadInnerXml();

Converting one XML document into another XML document

I want to convert an XML document containing many elements within a node (around 150) into another XML document with a slightly different schema but mostly with the same element names. Now do I have to manually map each element/node between the 2 documents. For that I will have to hardcode 150 lines of mapping and element names. Something like this:
XElement newOrder = new XElement("Order");
newOrder.Add(new XElement("OrderId", (string)oldOrder.Element("OrderId")),
newOrder.Add(new XElement("OrderName", (string)oldOrder.Element("OrderName")),
...............
...............
...............and so on
The newOrder document may contain additional nodes which will be set to null if nothing is found for them in the oldOrder. So do I have any other choice than to hardcode 150 element names like orderId, orderName and so on... Or is there some better more maintainable way?
Use an XSLT transform instead. You can use the built-in .NET XslCompiledTransform to do the transformation. Saves you from having to type out stacks of code. If you don't already know XSL/XSLT, then learning it is something that'll bank you CV :)
Good luck!
Use an XSLT transformation to translate your old xml document into the new format.
XElement.Add has an overload that takes object[].
List<string> elementNames = GetElementNames();
newOrder.Add(
elementNames
.Select(name => GetElement(name, oldOrder))
.Where(element => element != null)
.ToArray()
);
//
public XElement GetElement(string name, XElement source)
{
XElement result = null;
XElement original = source.Elements(name).FirstOrDefault();
if (original != null)
{
result = new XElement(name, (string)original)
}
return result;
}

Categories