I have an XML document using two XSD and I want to validate it.
First of all, I retrieve all XSD used and add them in an XmlSchemaSet.
Next, I read my document with an XmlReader. If this works perfectly with an Xml with only one namespace, It fails when I try to validate my XML with two namespace or more like this :
<element1>
<LMSG:element2>value</LMSG:element2>
</element1>
Do you have a work around for it ?
Thanks for your help
-EDIT-
here is my function to load all my XSD to validate my file (It retrieve the XSD depending on a country standards):
public XmlSchemaSet GetAllXSDUsed(String strCountrySpecific, String strCurrentXSDPath, XmlNode currentNode = null)
{
XmlSchemaSet xSet = new XmlSchemaSet();
if (currentNode == null)
currentNode = this;
if (!String.IsNullOrWhiteSpace(currentNode.NamespaceURI))
{
String strXsdName = currentNode.NamespaceURI.Substring(currentNode.NamespaceURI.LastIndexOf(':') + 1) + ".xsd";
String strPath = strCurrentXSDPath;
XmlSchema schema = null;
if (!String.IsNullOrWhiteSpace(strCountrySpecific) && File.Exists(Path.Combine(strPath, strCountrySpecific, strXsdName)))
schema = xSet.Add(currentNode.NamespaceURI, Path.Combine(strPath, strCountrySpecific, strXsdName));
else if (File.Exists(Path.Combine(strPath, strXsdName)))
schema = xSet.Add(currentNode.NamespaceURI, Path.Combine(strPath, strXsdName));
}
foreach (XmlNode node in currentNode.ChildNodes)
{
XmlSchemaSet newxSet = GetAllXSDUsed(strCountrySpecific, strCurrentXSDPath, currentNode: node);
if (newxSet.Count > 0)
foreach (XmlSchema xs in newxSet.Schemas())
if (!xSet.Contains(xs.TargetNamespace))
xSet.Add(xs);
}
return xSet;
}
The problem is maybe that I specify the prefix used nowhere.
Here is the error that I get : The element 'CBISDDReqLogMsg' has invalid child element 'GrpHdr' in namespace 'urn:CBI:xsd:CBISDDReqLogMsg.00.00.06'. List of possible elements expected: 'GrpHdr'. for this block :
<CBISDDReqLogMsg>
<LMSG:GrpHdr xmlns:LMSG="urn:CBI:xsd:CBISDDReqLogMsg.00.00.06">
<LMSG:MsgId>Value</LMSG:MsgId>
Note that the prefix 'LMSG' is also declared at the root of the document.
Related
I have this chunk of XML
<EnvelopeStatus>
<CustomFields>
<CustomField>
<Name>Matter ID</Name>
<Show>True</Show>
<Required>True</Required>
<Value>3</Value>
</CustomField>
<CustomField>
<Name>AccountId</Name>
<Show>false</Show>
<Required>false</Required>
<Value>10804813</Value>
<CustomFieldType>Text</CustomFieldType>
</CustomField>
I have this code below:
// TODO find these programmatically rather than a strict path.
var accountId = envelopeStatus.SelectSingleNode("./a:CustomFields", mgr).ChildNodes[1].ChildNodes[3].InnerText;
var matterId = envelopeStatus.SelectSingleNode("./a:CustomFields", mgr).ChildNodes[0].ChildNodes[3].InnerText;
The problem is, sometimes the CustomField with 'Matter ID' might not be there. So I need a way to find the element based on what 'Name is', i.e. a programmatic way of finding it. I can't rely on indexes being accurate.
You can use this code to read innertext from a specific element:
XmlDocument doc = new XmlDocument();
doc.Load("your.xml");
XmlNodeList Nodes= doc.SelectNodes("/EnvelopeStatus/CustomField");
if (((Nodes!= null) && (Nodes.Count > 0)))
{
foreach (XmlNode Level1 in Nodes)
{
if (Level1.ChildNodes[1].Name == "name")
{
string text = Convert.ToInt32(Level1.ChildNodes[1].InnerText.ToString());
}
}
}
You can often find anything in a XML document by utilizing the XPath capabilities that is available directly in the .NET Framework versions.
Maybe create a small XPath parser helper class
public class EnvelopeStatusParser
{
public XmlNodeList GetNodesWithName(XmlDocument doc, string name)
{
return doc.SelectNodes($"//CustomField[Name[text()='{name}']]");
}
}
and then call it like below to get all CustomFields which have a Name that equals what you need to search for
// Creating the XML Document in some form - here reading from file
XmlDocument doc = new XmlDocument();
doc.Load(#"envelopestatus.xml");
var parser = new EnvelopeStatusParser();
var matchingNodes = parser.GetNodesWithName(doc, "Matter ID");
Console.WriteLine(matchingNodes);
matchingNodes = parser.GetNodesWithName(doc, "NotHere");
Console.WriteLine(matchingNodes);
There exist numerous XPath cheat sheets around - like this one from LaCoupa - xpath-cheatsheet which can be quiet helpful to fully utilize XPath on XML structures.
The xml is coming from a url and all I need is the pull the string "N0014E1" from it. I am not sure why this code is not working. I put a try block around it and I get a "Data root level is invalid"
xml:
<obj is="c2g:Network " xsi:schemaLocation="http://obix.org/ns/schema/1.0/obi/xsd" href="http://192.168.2.230/obix/config/">
<ref name="N0014E1" is="c2g:LOCAL c2g:Node"xsi:schemaLocation="http://obix.org/ns/sc/1.0/obix/xsd" href="N0014E1/"></ref>
</obj>
C# code:
public static string NodePath = "http://" + MainClass.IpAddress + ObixPath;
public static void XMLData()
{
XmlDocument NodeValue = new XmlDocument();
NodeValue.LoadXml(NodePath);
var nodes = NodeValue.SelectNodes(NodePath);
foreach (XmlNode Node in nodes)
{
HttpContext.Current.Response.Write(Node.SelectSingleNode("//ref name").Value);
Console.WriteLine(Node.Value);
}
//Console.WriteLine(Node);
Console.ReadLine();
}
Your SelectNodes and SelectSingleNode commands are incorrect. Both expect an xpath string to identify the node.
Try the following
string xml = #"<obj is=""c2g:Network "" href=""http://192.168.2.230/obix/config/""><ref name=""N0014E1"" is=""c2g:LOCAL c2g:Node"" href=""N0014E1/""></ref></obj>";
XmlDocument NodeValue = new XmlDocument();
NodeValue.LoadXml(xml);
XmlNode r = NodeValue.SelectSingleNode("//ref[#name]");
if (r != null)
{
System.Diagnostics.Debug.WriteLine(r.Attributes["name"].Value);
}
Also, Note, that LoadXml method simply loads an xml string; it will not load from a remote url.
As #kevintdiy has pointed out your xml is not entirely correct. In the sample above I have stripped out the xsi reference as you are lacking a definition for it.
If you have access to the source xml, either remove the reference to xsi if its not required or add a definition for it to the root node.
If this is not possible, then you may want to consider using regular expression or other string based methods for getting the value.
everyone!
I have an XML file and need to change the value of a node, specifically the indicated line. The problem i have is that as you can see, there are many nodes.
How can i change this line? This XML file could be much larger, so i am looking for a solution that would take different amounts of 'launch.file' nodes into account.
The node that will need to be set to True will be identified by the corresponding NAME tag. So if i typed in ULTII, the DISABLED node for that block will be set to True. If i typed in Catl, then the DISABLED node for that block would be changed.
<?xml version="1.0" encoding="windows-1252"?>
<SBase.Doc Type="Launch" version="1,0">
<Descr>Launch</Descr>
<Filename>run.xml</Filename>
<Disabled>False</Disabled>
<Launch.ManualLoad>False</Launch.ManualLoad>
<Launch.File>
<Name>Catl</Name>
<Disabled>False</Disabled>
<ManualLoad>False</ManualLoad>
<Path>ft\catl\catl.exe</Path>
</Launch.File>
<Launch.File>
<Disabled>False</Disabled> <!-- change to True -->
<ManualLoad>False</ManualLoad>
<Name>ULTII</Name>
<Path>F:\ULTII.exe</Path>
<NewConsole>True</NewConsole>
</Launch.File>
<Launch.File>
<Name>ECA</Name>
<Disabled>False</Disabled>
<Path>C:\ECA.exe</Path>
</Launch.File>
</SBase.Doc>
I am using Visual Studio 2012, should you need to know.
Thank you to anyone who can help me out on this, i really appreciate it.
Heres my method to do what you want
private void DisableLaunchFile(string xmlfile, string launchFileName){
XDocument doc = XDocument.Load(xmlfile);
var launchFileElement = doc.Descendants("Launch.File").Where (d => d.Element("Name").Value == lauchFileName);
launchFileElement.Elements("Disabled").First().Value = true.ToString();
doc.Save(xmlfile);
}
Use it like:
string pathToXmlFile = //assign ;
DisableLaunchFile(pathToXmlFile, "Catl");
DisableLaunchFile(pathToXmlFile, "ULTII");
This can be achieved by using LINQ to XML (see XDocument Class).
Assuming that there is the single Launch.File element with Name element with value "ULTII":
var document = XDocument.Load(...);
var ultiiElement = document
.Descendants("Launch.File")
.Single(fileElement => fileElement.Element("Name").Value == "ULTII");
ultiiElement.Element("Disabled").Value = "True"; // or true.ToString()
document.Save(...);
This method will do the trick:
public void ChangeNode(string name, string filePath)
{
XDocument xDocument;
using (var streamReader = new StreamReader(filePath))
{
xDocument = XDocument.Parse(streamReader.ReadToEnd());
}
var nodes = xDocument.Descendants("Launch.File");
foreach (var node in nodes)
{
var nameNode = node.Descendants("Name").FirstOrDefault();
if (nameNode != null && nameNode.Value == name)
{
var disabledNode = node.Descendants("Disabled").FirstOrDefault();
if (disabledNode != null)
{
disabledNode.SetValue("True");
}
}
}
using (var streamWriter = new StreamWriter(filePath))
{
xDocument.Save(streamWriter);
}
}
The name you want to pass in is the name of the node that you want to change and the path is the file path to the xml file. So you might call it like:
ChangeNode("ULTII", "C:\\output.xml");
You may need to tidy this up a bit like matching the node name invariant of case or culture but it should get you started.
I have an XML string that is loaded into an XMLDocument, similar to the one listed below:
<note>
<to>You</to>
<from>Me</from>
<heading>TEST</heading>
<body>This is a test.</body>
</note>
I would like to convert the text nodes to attributes (using C#), so it looks like this:
<note to="You" from="Me" heading="TEST" body="This is a test." />
Any info would be greatly appreciated.
Linq to XML is great for this kind of stuff. You could probably achieve it in one line if you'd want to. Just grab the child node names and their respective value and add all those 'key value pairs' as attributes instead.
MSDN docs here: http://msdn.microsoft.com/en-us/library/bb387098.aspx
Like seldon suggests, LINQ to XML would be a much better fit for this sort of task.
But here's a way to do it with XmlDocument. Not claiming this is fool-proof (haven't tested it), but it does seem to work for your sample.
XmlDocument input = ...
// Create output document.
XmlDocument output = new XmlDocument();
// Create output root element: <note>...</note>
var root = output.CreateElement(input.DocumentElement.Name);
// Append attributes to the output root element
// from elements of the input document.
foreach (var child in input.DocumentElement.ChildNodes.OfType<XmlElement>())
{
var attribute = output.CreateAttribute(child.Name); // to
attribute.Value = child.InnerXml; // to = "You"
root.Attributes.Append(attribute); // <note to = "You">
}
// Make <note> the root element of the output document.
output.AppendChild(root);
Following converts any simple XML leaf node into an attribute of its parent. It is implemented as a unit test. Encapsulate your XML content into an input.xml file, and check it saved as output.xml.
using System;
using System.Xml;
using System.Linq;
using NUnit.Framework;
[TestFixture]
public class XmlConvert
{
[TestCase("input.xml", "output.xml")]
public void LeafsToAttributes(string inputxml, string outputxml)
{
var doc = new XmlDocument();
doc.Load(inputxml);
ParseLeafs(doc.DocumentElement);
doc.Save(outputxml);
}
private void ParseLeafs(XmlNode parent)
{
var children = parent.ChildNodes.Cast<XmlNode>().ToArray();
foreach (XmlNode child in children)
if (child.NodeType == XmlNodeType.Element
&& child.Attributes.Count == 0
&& child.ChildNodes.Count == 1
&& child.ChildNodes[0].NodeType == XmlNodeType.Text
&& parent.Attributes[child.Name] == null)
{
AddAttribute(parent, child.Name, child.InnerXml);
parent.RemoveChild(child);
}
else ParseLeafs(child);
// show no closing tag, if not necessary
if (parent.NodeType == XmlNodeType.Element
&& parent.ChildNodes.Count == 0)
(parent as XmlElement).IsEmpty = true;
}
private XmlAttribute AddAttribute(XmlNode node, string name, string value)
{
var attr = node.OwnerDocument.CreateAttribute(name);
attr.Value = value;
node.Attributes.Append(attr);
return attr;
}
}
Here is my XML file
<?xml version="1.0" encoding="utf-8" ?>
<storage>
<Save Name ="Lifeline">
<Seconds>12</Seconds>
<Minutes>24</Minutes>
<Hours>9</Hours>
<Days>25</Days>
<Months>8</Months>
<Years>2010</Years>
<Health>90</Health>
<Mood>100</Mood>
</Save>
<Save Name ="Hellcode">
<Seconds>24</Seconds>
<Minutes>48</Minutes>
<Hours>18</Hours>
<Days>15</Days>
<Months>4</Months>
<Years>1995</Years>
<Health>50</Health>
<Mood>50</Mood>
</Save>
Here is a code which get's data from XML and loads it into application.
System.IO.StreamReader sr = new System.IO.StreamReader(#"Saves.xml");
System.Xml.XmlTextReader xr = new System.Xml.XmlTextReader(sr);
System.Xml.XmlDocument save = new System.Xml.XmlDocument();
save.Load(xr);
XmlNodeList saveItems = save.SelectNodes("Storage/Save");
XmlNode seconds = saveItems.Item(0).SelectSingleNode("Seconds");
sec = Int32.Parse(seconds.InnerText);
XmlNode minutes = saveItems.Item(0).SelectSingleNode("Minutes");
min = Int32.Parse(minutes.InnerText);
XmlNode hours = saveItems.Item(0).SelectSingleNode("Hours");
hour = Int32.Parse(hours.InnerText);
XmlNode days = saveItems.Item(0).SelectSingleNode("Days");
day = Int32.Parse(days.InnerText);
XmlNode months = saveItems.Item(0).SelectSingleNode("Months");
month = Int32.Parse(months.InnerText);
XmlNode years = saveItems.Item(0).SelectSingleNode("Years");
year = Int32.Parse(years.InnerText);
XmlNode health_ = saveItems.Item(0).SelectSingleNode("Health");
health = Int32.Parse(health_.InnerText);
XmlNode mood_ = saveItems.Item(0).SelectSingleNode("Mood");
mood = Int32.Parse(mood_.InnerText);
The problem is that this code loads data inly from "Lifeline" node. I would like to use a listbox and be able to choose from which node to load data.
I've tried to take string from listbox item content and then use such a line
XmlNodeList saveItems = save.SelectNodes(string.Format("storage/Save[#Name = '{0}']", name));
variable "name" is a string from listboxe's item. While compiled this code gives exception.
Do somebody knows a way how to select by attribute and load nedeed data from that XML?
If you can use XElement:
XElement xml = XElement.Load(file);
XElement storage = xml.Element("storage");
XElement save = storage.Elements("Save").FirstOrDefault(e => ((string)e.Attribute("Name")) == nameWeWant);
if(null != save)
{
// do something with it
}
Personally I like classes that have properties that convert to and from the XElement to hide that detail from the main program. IE say the Save class takes an XElement node in the constructor, saves it internally globally, and the properties read/write to it.
Example class:
public class MyClass
{
XElement self;
public MyClass(XElement self)
{
this.self = self;
}
public string Name
{
get { return (string)(self.Attribute("Name") ?? "some default value/null"); }
set
{
XAttribute x = source.Attribute("Name");
if(null == x)
source.Add(new XAttribute("Name", value));
else
x.ReplaceWith(new XAttribute("Name", value));
}
}
}
Then you can change the search to something like:
XElement save = storage.Elements("Save")
.FirstOrDefault(e => new MyClass(e).Name == NameWeWant);
Since it is not that much data, I'd suggest loading all information to a list of saves(constructor) and then drawing from there which one the user would like to use...
As for things not working, I personally use a lower level approach to get my data and it is not error prone. Remodeling it to fit your problem a bit:
int saves = 0;
List<Saves> saveGames = new List<Saves>();
saveGames.Add(new Saves());
while (textReader.Read())
{
if (textReader.NodeType == XmlNodeType.Element)
whatsNext = textReader.Name;
else if (textReader.NodeType == XmlNodeType.Text)
{
if (whatsNext == "name")
saveGames[saves].name = Convert.ToString(textReader.Value);
//else if statements for the rest of your attributes
else if (whatsNext == "Save")
{
saveGames.Add(new Saves());
saves++;
}
}
else if (textReader.NodeType == XmlNodeType.EndElement)
whatsNext = "";
}
Basically throw everything in the xml file into a list of objects and manipulate that list to fill the listbox. Instead of having Saves name = "...", have a name attribute as the first attribute in the save.
Code tags hate me. Why they break so easily ( ._.)
The select nodes is returning two XmlNode objects.
XmlNodeList saveItems = save.SelectNodes("Storage/Save");
Later in your code you seem to be selecting the first one and with saveItems.Item(0) and getting values from it which in this case would be the save node with the Name="LifeLine". So if you were to do saveItems.Item(1) and select nodes and its values then you would get the other set of nodes.