I need to process the data in an XML file similar to this:
<?xml version="1.0" encoding="utf-8"?>
<bpr:release xmlns:bpr="http://www.bp.co.uk/product/release">
<bpr:contents>
<process id="1" name="Process 1" xmlns="http://www.bp.co.uk/product/process">
<process name="Process 1"></process>
</process>
<process id="2" name="Process 2" xmlns="http://www.bp.co.uk/product/process">
<process name="Process 1"></process>
</process>
<object id="1" name="Object 1" xmlns="http://www.bp.co.uk/product/process">
<process name="Object 1" ></process>
</object>
<work-queue id="1" name="Queue1" xmlns="http://www.bp.co.uk/product/work-queue"/>
<process-group id="1" xmlns="http://www.bp.co.uk/product/process-group">
<members>
<process id="1"/>
<process id="2"/>
</members>
</process-group>
<object-group id="1" xmlns="http://www.bp.co.uk/product/object-group">
<members>
<object id="1"/>
<object id="2"/>
</members>
</object-group>
</bpr:contents>
</bpr:release>
For example, I need to extract the /bpr:release/bpr:contents/process/process nodes for analysis, but I get nulls or this error:
System.Xml.XPath.XPathException
HResult=0x80131943
Message=Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function.
Source=System.Xml
I see that the file has multiple anonymous namespaces, and there is the same namespace used twice. I've tried various combinations, for example:
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlPath);
var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("bpr", "http://www.bp.co.uk/product/release");
nsmgr.AddNamespace("bpp", "http://www.bp.co.uk/product/process");
nsmgr.AddNamespace("bpo", "http://www.bp.co.uk/product/process");
nsmgr.AddNamespace("bpwq", "http://www.bp.co.uk/product/work-queue");
nsmgr.AddNamespace("bppg", "http://www.bp.co.uk/product/process-group");
nsmgr.AddNamespace("bpog", "http://www.bp.co.uk/product/object-group");
XmlNodeList p;
XmlNodeList r;
try { r = xmlDoc.SelectNodes("/bpr:release"); } catch { }
try { r = xmlDoc.SelectNodes("//bpr:release"); } catch { }
try { r = xmlDoc.SelectNodes("bpr:release"); } catch { }
try { r = xmlDoc.SelectNodes("/release"); } catch { }
try { r = xmlDoc.SelectNodes("//release"); } catch { }
try { r = xmlDoc.SelectNodes("release"); } catch { }
try { p = xmlDoc.SelectNodes("process"); } catch { }
try { p = xmlDoc.SelectNodes("bpp:process"); } catch { }
try { p = xmlDoc.SelectNodes("/process"); } catch { }
try { p = xmlDoc.SelectNodes("/bpp:process"); } catch { }
try { p = xmlDoc.SelectNodes("//process"); } catch { }
try { p = xmlDoc.SelectNodes("//bpp:process"); } catch { }
try { p = xmlDoc.SelectNodes("/bpr:release/bpr:contents/process/process"); } catch { }
What is the correct XPath to get each of the child nodes of bpr:release, bpr:contents, process/process, object/process, etc.? How to use the XMLNamespaceManager for this?
You're adding namespace prefix declarations to your XmlNamespaceManager, but you're not using nsmgr in your calls to SelectNodes().
try { r = xmlDoc.SelectNodes("/bpr:release", nsmgr); } catch { }
^^^^^^^
Fix up the other calls accordingly.
Note also that your first example XPath,
/bpr:release/bpr:contents/process/process
should be
/bpr:release/bpr:contents/bpp:process/bpp:process
given your declaration:
nsmgr.AddNamespace("bpp", "http://www.bp.co.uk/product/process");
Finally, you should remove duplicate prefix declarations (e.g. bpp/bpo) in some of the AddNamespace() calls.
See also
How does XPath deal with XML namespaces?
Difference between "//" and "/" in XPath?
Related
I'm developing my first WPF application with C# but I have a problem when I'm trying to read a Xml attribute.
I have the following Xml:
<?xml version="1.0" encoding="utf-8"?>
<Dictionary EnglishName="Italian" CultureName="Italian" Culture="">
<!-- MainWindow -->
<Value ID="WpfApplication1.MainWindow.BtnDrawCircle" Content="Circonferenza"/>
<Value ID="WpfApplication1.MainWindow.BtnDrawLine" Content="Linea"/>
....
....
</Dictionary>`
Now I try to get the Attribute "Content" with the following method:
public static string ReadNodeAttribute(string IDAttribute)
{
try
{
XmlDocument _Doc = new XmlDocument();
_Doc.Load("myPath\\Language\\it-IT.xml");
string _Value = _Doc.SelectSingleNode("//Value[#ID=" + IDAttribute + "]").Attributes["Content"].Value.ToString();
return _Value;
}
catch (Exception ex)
{
return null;
}
}
But it doesn't work:
Error : ex {"Object reference not set to an instance of an object."} System.Exception {System.NullReferenceException}
You got
null reference exception
because you didn't check for null in case your IDAttribute doesn't exist in the XML.
Just change to your path and it will work.
using System;
using System.Linq;
using System.Xml.Linq;
public static string ReadNodeAttribute(string IDAttribute)
{
string _Value = "";
try
{
//I used System.IO.Path.GetFullPath because I tried it with ConsoleApplication.
//Use what ever work for you to load the xml.
XDocument xdoc = XDocument.Load(System.IO.Path.GetFullPath("XMLFile1.xml"));
var myValue = xdoc.Descendants("Value").FirstOrDefault(i => i.Attribute("ID").Value == IDAttribute);
if (myValue != null)
{
_Value = myValue.Attribute("Content").Value;
return _Value;
}
}
catch (Exception ex)
{
return null;
}
return _Value;
}
I have tried using Linq to Xml
XDocument xdoc = XDocument.Load(Server.MapPath("path"));
var val = xdoc.Descendants("Value").Where(i => i.Attribute("ID").Value == IDAttribute).FirstOrDefault().Attribute("Content").Value;
Inorder to use this you have to include System.Xml.Linq namespace
so I'm a total noob to C# is there any way to make this work?
It would also be a great help if someone could explain why my system doesn't work, and why another version would.
using System;
using System.Xml;
public class XMLManager
{
private XmlTextReader reader;
private XmlDocument document;
private XmlNodeList nodeList;
public void OpenFile(string file)
{
try
{
reader = new XmlTextReader(file);
reader.WhitespaceHandling = WhitespaceHandling.None;
reader.MoveToContent();
document = new XmlDocument();
document.Load(reader);
nodeList = document.SelectNodes(#"Settings/Settings");
}
catch (System.IO.FileNotFoundException)
{
}
}
public void CloseFile()
{
if (reader != null)
{
((IDisposable)reader).Dispose();
reader.Close();
reader = null;
}
document = null;
nodeList = null;
}
public string Get(string attrib)
{
for (int i = 0; i < nodeList.Count; i++)
{
reader.MoveToAttribute(i);
if (reader.Name == attrib)
{
return reader.Value;
}
}
return null;
}
}
Edit: Sorry for my bad formatting, this is my first time posting on Stack Overflow.
You are making multiple mistakes here.
First of all, you don't need a reader to read xml content into an XmlDocument.
Second, while trying to get the attributes, you are trying to proceed to the attributes using the reader which is obviously not having the context of the selected nodes.
Here is your updated XmlManager, but I have to note, there is a logical error too, which is, when the GetAttribute is invoked, you are searching all the Settings/Settings nodes and if find the attribute in any of them, return it. If the xml file contains only one Settings/Settings node, SelectSingleNode is better. I assume the following format:
<Settings>
<Settings attr1="attr1val" attr2="attr2val" />
</Settings>
Note: I also removed CloseFile method because it is no longer required.
public class XMLManager
{
private XmlDocument document;
private XmlNodeList nodeList;
public void OpenFile(string file)
{
document = new XmlDocument();
document.Load(file);
nodeList = document.SelectNodes(#"Settings/Settings");
}
public string Get(string attrib)
{
for (int i = 0; i < nodeList.Count; i++)
{
if (nodeList[i].Attributes[attrib] != null)
{
return nodeList[i].Attributes[attrib].Value;
}
}
return null;
}
}
Overall, you're doing way too much work.
If you have XML in a file, load it directly into an XML (DOM) object using XmlDocument.Load( strFileName );
To iterate all nodes matching an XPath query, see how I run through them.
try {
string strFileName = HttpContext.Current.Server.MapPath("\\data.xml");
XmlDocument xml = new XmlDocument();
xml.Load( strFileName );
foreach (XmlElement ndRow in xml.SelectNodes("//row")) {
string strTemp = ndRow.GetAttribute("foo");
}
} catch (Exception ex) {
Response.Write(ex.Message);
}
I have a xml file which looks like below.
<sections>
<section>
<name>QLD Mosques</name>
<stations>
<station>
<id>101</id>
<pn>true</pn>
<name>Kuraby Mosque</name>
<url>http://sb2110.ultrastream.co.uk/kuraby</url>
<icon>...</icon>
</station>
<station>
<id>102</id>
<pn>true</pn>
<name>Gold Coast Mosque</name>
<url>http://sb2110.ultrastream.co.uk/goldcoast</url>
<icon>http://www.juju.net.au/mosquereceivers/images/icons/gc.jpg</icon>
</station>
<station>...</station>
</stations>
</section>
<section>
<name>NZ Mosques</name>
<stations>...</stations>
</section>
<section>
<name>Islamic Radio Stations</name>
<stations>...</stations>
</section>
</sections>
I want get show all the station name which has "section" named "QLD Mosques".
For example my result will be "Kuraby Mosque,Gold Coast Mosque,...".
How can i achieve the result??
N:B:
I can show the names under "section" tag(which gives the result QLD Mosques,NZ Mosques,Islamic Radio Stations) by using these code:
public static List<MyData> channel_main_list = new List<MyData>();
public MainChannelList()
{
InitializeComponent();
WebClient client = new WebClient();
client.OpenReadCompleted += client_OpenReadCompleted;
client.OpenReadAsync(new Uri("http://www.juju.net.au/mosquereceivers/Stations.xml",UriKind.Absolute));
}
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
return;
Stream str = e.Result;
string node = "section";
XDocument loadedData = XDocument.Load(str);
try
{
foreach (var item in loadedData.Descendants(node))
{
try
{
MyData m = new MyData();
m.channel_name = item.Element("name").Value;
channel_main_list.Add(m);
}
catch (Exception)
{
MessageBox.Show("Problem");
}
}
listBox.ItemsSource = channel_main_list;
}
catch (Exception)
{
MessageBox.Show("Connectivity Problem");
}
}
This is one possible way, assuming that the XML structure is consistent and name being searched is always found :
var result = loadedData.Descendants("section")
.Where(o => (string)o.Element("name") == "QLD Mosques")
.Elements("stations")
.Elements("station")
.Elements("name");
Dotnetfiddle Demo
I am loading MusicXML-files into my program. The problem: There are two “dialects”, timewise and partwise, which have different root-nodes (and a different structure):
<?xml version="1.0" encoding='UTF-8' standalone='no' ?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 2.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="2.0">
<work>...</work>
...
</score-partwise>
and
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE score-timewise PUBLIC "-//Recordare//DTD MusicXML 2.0 Timewise//EN" "http://www.musicxml.org/dtds/timewise.dtd">
<score-timewise version="2.0">
<work>...</work>
...
</score-timewise>
My code for deserializing the partwise score so far is:
using (var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open))
{
var xmlSerializer = new XmlSerializer(typeof(ScorePartwise));
var result = (ScorePartwise)xmlSerializer.Deserialize(fileStream);
}
What would be the best way to differentiate between the two dialects?
Here's a way to do it by using an XDocument to parse the file, read the root element to determine the type, and read it into your serializer.
var xdoc = XDocument.Load(filePath);
Type type;
if (xdoc.Root.Name.LocalName == "score-partwise")
type = typeof(ScorePartwise);
else if (xdoc.Root.Name.LocalName == "score-timewise")
type = typeof(ScoreTimewise);
else
throw new Exception();
var xmlSerializer = new XmlSerializer(type);
var result = xmlSerializer.Deserialize(xdoc.CreateReader());
I would create both serializers
var partwiseSerializer = new XmlSerializer(typeof(ScorePartwise));
var timewiseSerializer = new XmlSerializer(typeof(ScoreTimewise));
Assuming that there is only these two I would call CanDeserialize method on one
using (var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open))
{
using (var xmlReader = XmlReader.Create(filStream))
{
if (partwiseSerializer.CanDeserialize(xmlReader))
{
var result = partwiseSerializer.Deserialize(xmlReader);
}
else
{
var result = timewiseSerializer.Deserialize(xmlReader);
}
}
}
Obviously this is just an idea how to do it. If there were more options or according to your application design I would use a more sophisticated way to call CanDeserialize, but that method is the key in my opinion:
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.candeserialize.aspx
The XmlReader class can be found here:
http://msdn.microsoft.com/en-us/library/System.Xml.XmlReader(v=vs.110).aspx
If you're concerned about resource usage:
internal const string NodeStart = "<Error ";
public static bool IsErrorDocument(string xml)
{
int headerLen = 1;
if (xml.StartsWith(Constants.XMLHEADER_UTF8))
{
headerLen += Constants.XMLHEADER_UTF8.Length;
}
else if (xml.StartsWith(Constants.XMLHEADER_UTF16))
{
headerLen += Constants.XMLHEADER_UTF16.Length;
}
else
{
return false;
}
if (xml.Length < headerLen + NodeStart.Length)
{
return false;
}
return xml.Substring(headerLen, NodeStart.Length) == NodeStart;
}
internal class Constants
{
public const string XMLHEADER_UTF16 = "<?xml version=\"1.0\" encoding=\"utf-16\"?>";
public const string XMLHEADER_UTF8 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
}
I have one problem. I have to serialize an object and that sam object is converted to XML, but it's a datetime object.
Ok, i have string variable which is filled with data from some sql query, like this
//DateDeliveryRequest
if (rw_mat["dat_pot"].ToString().Length <= 0)
{
date_req = "";
}
else
{
date_req = rw_mat["dat_pot"].ToString();
}
Now, date_req is being passet to object like this:
var dtfi = new DateTimeFormatInfo { ShortDatePattern = "dd-MM-yyyy", DateSeparator = "-" };
Agr3PL.DataArea.Header.DateDeliveryRequest = Convert.ToDateTime(date_req, dtfi);
And at the end this object with among other is being passed to serialize function:
private string SerializeAnObject(object obj)
{
System.Xml.XmlDocument doc = new XmlDocument();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
System.IO.MemoryStream stream = new System.IO.MemoryStream();
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
return doc.InnerXml;
}
catch (Exception ex)
{
WriteErrors.WriteToLogFile("WS.SAVE_DOK_SERIJALIZACIJA", ex.ToString());
throw ex;
}
So i want to get datetime variable to xml but I don't, the xml has to look something like this(It is just a segment)
- <Header>
<MessageType>COR</MessageType>
<UniqueHeaderNumber />
<UniqueHeaderNumberFromWMS />
<CompanyCode />
<OrderNumber />
<InvoiceNumber />
<MovementNumber />
<ReceiptNumber />
<DebitNoteNumber />
<PickNoteNumber />
<LoadNumber />
<DropSequence />
<BulkPickNoteNumber />
<NumberOfPallets />
<DateDeliveryRequest />
But the problem is that the xml is not showing the node >DateDeliveryRequest it's not there
I don't get error just there is no node >DateDeliveryRequest???
I don't know is it the problem in serializer function or on something else, maybe in formating date or something else?
I tested this code:
public class Test
{
private DateTime dateDeliveryRequestField;
[System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
public System.DateTime DateDeliveryRequest { get { return this.dateDeliveryRequestField; } set { this.dateDeliveryRequestField = value; } }
}
private string SerializeAnObject(object obj)
{
System.Xml.XmlDocument doc = new XmlDocument();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
System.IO.MemoryStream stream = new System.IO.MemoryStream();
try
{
serializer.Serialize(stream, obj);
stream.Position = 0;
doc.Load(stream);
return doc.InnerXml;
}
catch (Exception ex)
{
throw ex;
}
}
Test n = new Test();
n.DateDeliveryRequest = DateTime.Parse("2012-10-07");
string result = SerializeAnObject(n);
and result:
<?xml version="1.0"?>
<Test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<DateDeliveryRequest>2012-10-07</DateDeliveryRequest>
</Test>
So everything looks good.
To serialize dates use this string format
date_req = rw_mat["dat_pot"].ToString("s");