C# Skip anything to next tag - c#

I have a log file in xml format like
<log> // skip this node
<?xml version="1.0" encoding="UTF-8"?>
<qbean logger="main-logger">
</qbean>
</log>
<log> // go to this node
</log>
Now ReadToNextSibling("log") throw an exception an I need to skip content of first "log" tag and move to next "log" tag without throwing exception.
Is there a way?

Hint:
Your XML is invalid since the <?xml version="1.0" encoding="UTF-8"?> has to be before the root element. You can search for it and remove it if that fixes your problem. You can use yourXml.Repalce("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "")
You have to create a root element for your XML to be valid for parsing.
Then, you can use the XmlDocument class to parse the XML data that you have and skip anything you want. You would need something like this:
var document = new XmlDocument();
document.LoadXml(yourXml);
document.DocumentElement.ChildNodes[1]

Related

How to parse a XML with nested XML text

Trying to read XML file with nested XML object with own XML declaration. As expected got exception:
Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it.
How can i read that specific element as text and parse it as separate XML document for later deserialization?
<?xml version="1.0" encoding="UTF-8"?>
<Data>
<Items>
<Item>
<Target type="System.String">Some target</Target>
<Content type="System.String"><?xml version="1.0" encoding="utf-8"?><Data><Items><Item><surname type="System.String">Some Surname</surname><name type="System.String">Some Name</name></Item></Items></Data></Content>
</Item>
</Items>
</Data>
Every approach i'm trying fail due to declaration exception.
var xml = System.IO.File.ReadAllText("Info.xml");
var xDoc = XDocument.Parse(xml); // Exception
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xml); // Exception
var xmlReader = XmlReader.Create(new StringReader(xml));
xmlReader.ReadToFollowing("Content"); // Exception
I have no control over XML creation.
The only way I would know is by getting rid of the illegal second <?xml> declaration. I wrote a sample that will simply look for and discard the second <?xml>. After that the string has become valid XML and can be parsed. You may need to tweak it a bit to make it work for your exact scenario.
Code:
using System;
using System.Xml;
public class Program
{
public static void Main()
{
var badXML = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<Data>
<Items>
<Item>
<Target type=""System.String"">Some target</Target>
<Content type=""System.String""><?xml version=""1.0"" encoding=""utf-8""?><Data><Items><Item><surname type=""System.String"">Some Surname</surname><name type=""System.String"">Some Name</name></Item></Items></Data></Content>
</Item>
</Items>
</Data>";
var goodXML = badXML.Replace(#"<Content type=""System.String""><?xml version=""1.0"" encoding=""utf-8""?>"
, #"<Content type=""System.String"">");
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(goodXML);
XmlNodeList itemRefList = xmlDoc.GetElementsByTagName("Content");
foreach (XmlNode xn in itemRefList)
{
Console.WriteLine(xn.InnerXml);
}
}
}
Output:
<Data><Items><Item><surname type="System.String">Some Surname</surname><name type="System.String">Some Name</name></Item></Items></Data>
Working DotNetFiddle: https://dotnetfiddle.net/ShmZCy
Perhaps needless to say: all of this would not have been needed if the thing that created this invalid XML would have applied the common rule to wrap the nested XML in a <![CDATA[ .... ]]> block.
The <?xml ...?> processing declaration is only valid on the first line of an XML document, and so the XML that you've been given isn't well-formed XML. This will make it quite difficult to parse as is without either changing the source document (and you've indicated that's not possible) or preprocessing the source.
You could try:
Stripping out the <?xml ?> instruction with regex or string manipulation, but the cure there may be worse than the disease.
The HTMLAgilityPack, which implements a more forgiving parser, may work with an XML document
Other than that, the producer of the document should look to produce well-formed XML:
CDATA sections can help this, but be aware that CDATA can't contain the ]]> end tag.
XML escaping the XML text can work fine; that is, use the standard routines to turn < into < and so forth.
XML namespaces can also help here, but they can be daunting in the beginning.

How to comment out line by line of a XML file in C# with System.XML

I need to comment and uncomment a XML node with child nodes in a file using System.XML.
Starting XML:
<?xml version="1.0" encoding="utf-8"?>
<test>
<!--comment...-->
<childTest>
<childchildTest>5<childchildTest/>
</childTest>
</test>
Commenting the whole node wouldn't be a problem and easy to achieve like in this example. But my problem is that I've already got some comments inside the node and nested comments aren't allowed per XML rules.
That means I would have to comment out line by line of the XML file so I would not destroy the XML file structure with nested comments.
Desired output:
<?xml version="1.0" encoding="utf-8"?>
<!-- <test> -->
<!--comment...-->
<!-- <childTest> -->
<!-- <childchildTest>5<childchildTest/> -->
<!-- </childTest> -->
<!-- </test> -->
Is it possible to achieve this with System.XML or would I have to do this with regex for example?
Assuming you have this XML well-organised in a file (meaning each node is on its own line, as you presented) you could use this one-liner:
File.WriteAllLines("path to new XML file", File.ReadAllLines("path to XML file").Select(line => line.Trim().StartsWith("<!--") ? line : $"<!--{line}-->"));
This part line.Trim().StartWith("<!--") ? line : $"<!--{line}-->" means if line is a comment (starts with <!--) then don't comment it, otherwise, do it.
In my opinion, there is no framework method which provides this functionality.
You can read XML file lines and then create new files which has comments for every line as shown in below code.
// Create a string array with the lines of text
string[] lines = File.ReadAllLines(path-of-file);
// Write the string array to a new file named "ouput.xml".
using (StreamWriter outputFile = new StreamWriter(Path.Combine(mydocpath,"output.xml"))) {
foreach (string line in lines)
outputFile.WriteLine("<!--" + line + "-->");
}

Incorrect structured XML document after deleting element

When I try to load a XML file after deleting an Element from the file, it shows the following error: Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it. Line 9, position 10. What is wrong with my code?
This is my XML file:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<booze>booze1</booze>
<booze>booze2</booze>
<booze>booze3</booze>
<booze>booze4</booze>
</data>
And my code:
using (IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("favorites.xml", FileMode.Open, file))
{
XDocument xDoc = XDocument.Load(stream, LoadOptions.None);
// delete node
xDoc.Descendants("data").Elements("booze").Where(x => x.Value == favorite).DescendantsAndSelf().Remove();
xDoc.Save(stream);
}
}
My guess is that when you run the code for the first time, the delete succeeds and your XDoc contains :
<?xml version="1.0" encoding="utf-8"?>
<data>
<booze>booze1</booze>
<booze>booze2</booze>
<booze>booze4</booze>
</data>
, but when calling XDoc.Save you simply append this to the favorites.xml file. After that, the favorites.xml file contains something like this:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<booze>booze1</booze>
<booze>booze2</booze>
<booze>booze3</booze>
<booze>booze4</booze>
</data><?xml version="1.0" encoding="utf-8"?>
<data>
<booze>booze1</booze>
<booze>booze2</booze>
<booze>booze4</booze>
</data>
That is why all the subsequent file loads throw the error. What you should do is overwrite the file, and not append to it. The first way of doing that that comes to mind is to close the stream, open it with Mode.Truncate and then save the XDoc. Or you can delete and recreate the file. I am not that familiar with IsolatedStorageFiles so this is a wild guess.

why XElement cannot save my xml file to the file?

I want to construct a long xml string and some of its entities are from another xml file read by a dll. In the end, I'd like to save this xml string to a file by XElement.Save(). It cannot save the string to the file.
For example:
XElement root = new XElement("Root");
// .....
root.Save(filename); // <-- wrong!
However, If I do not use the dll, everything is fine. Even I just call the dll and do nothing else, it won't work for me. Can anybody help me? Thanks
For Appending Node into existing XML File:
From beginning,
1) Create one Root.xml file:
<?xml version="1.0" encoding="utf-8"?>
<Main>
</Main>
2) Use this code to Load and Append Nodes:
XElement xml = new XElement("Root");
XDocument xdoc = XDocument.Load("Root.xml");
xdoc.Element("Main").Nodes().Last().AddAfterSelf(xml); //append after the last backup element
xdoc.Save("Root.xml");

C# XmlDocument Nodes

I'm trying to access UPS tracking info and, as per their example, I need to build a request like so:
<?xml version="1.0" ?>
<AccessRequest xml:lang='en-US'>
<AccessLicenseNumber>YOURACCESSLICENSENUMBER</AccessLicenseNumber>
<UserId>YOURUSERID</UserId>
<Password>YOURPASSWORD</Password>
</AccessRequest>
<?xml version="1.0" ?>
<TrackRequest>
<Request>
<TransactionReference>
<CustomerContext>guidlikesubstance</CustomerContext>
</TransactionReference>
<RequestAction>Track</RequestAction>
</Request>
<TrackingNumber>1Z9999999999999999</TrackingNumber>
</TrackRequest>
I'm having a problem creating this with 1 XmlDocument in C#. When I try to add the second:
<?xml version="1.0" ?> or the <TrackRequest>
it throws an error:
System.InvalidOperationException: This
document already has a
'DocumentElement' node.
I'm guessing this is because a standard XmlDocument would only have 1 root node. Any ideas?
Heres my code so far:
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
XmlElement rootNode = xmlDoc.CreateElement("AccessRequest");
rootNode.SetAttribute("xml:lang", "en-US");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
XmlElement licenseNode = xmlDoc.CreateElement("AccessLicenseNumber");
XmlElement userIDNode = xmlDoc.CreateElement("UserId");
XmlElement passwordNode = xmlDoc.CreateElement("Password");
XmlText licenseText = xmlDoc.CreateTextNode("mylicense");
XmlText userIDText = xmlDoc.CreateTextNode("myusername");
XmlText passwordText = xmlDoc.CreateTextNode("mypassword");
rootNode.AppendChild(licenseNode);
rootNode.AppendChild(userIDNode);
rootNode.AppendChild(passwordNode);
licenseNode.AppendChild(licenseText);
userIDNode.AppendChild(userIDText);
passwordNode.AppendChild(passwordText);
XmlElement rootNode2 = xmlDoc.CreateElement("TrackRequest");
xmlDoc.AppendChild(rootNode2);
An XML document can only ever have one root node. Otherwise it's not well formed. You will need to create 2 xml documents and join them together if you need to send both at once.
Its throwing an exception because you are trying to create invalid xml. XmlDocument will only generate well formed xml.
You could do it using an XMLWriter and setting XmlWriterSettings.ConformanceLevel to Fragment or you could create two XmlDocuments and write them out into the same stream.
Build two separate XML documents and concatenate their string representation.
It looks like your node structure always be the same. (I don't see any conditional logic.) If the structure is constant you could define an XML template string. Load that string into an XML Document & do a SelectNode to populate individual nodes.
That may be simpler/cleaner than programatically creating the root, elements & nodes.

Categories