xml XDocument Save modify file - c#

I am trying to add XElements to an XDocument. I am trying to move from using inifile to xml but I am having some issues here.
This is my C# code:
public static async Task<List<XElement>> XMLHandling()
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile file = await storageFolder.GetFileAsync("sFile.xml");
List<XElement> doc;
using (var fileStream = await file.OpenStreamForReadAsync())
{
doc = XDocument.Load(fileStream).Descendants("signals").ToList();
//avoid range check
doc[0].Add(new XElement("Character", new XElement("Value", "value1"), new XElement("Description", "This is some text")));
using (Stream sr = await file.OpenStreamForWriteAsync())
{
doc[0].Save(sr);
}
}
//example
return doc;
}
This is how the file look before:
<?xml version="1.0"?>
<catalog>
<signals>
</signals>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
</catalog>
and this is how it becomes after:
<?xml version="1.0" encoding="utf-8"?>
<signals>
<Character>
<Value>value1</Value>
<Description>This is some text</Description>
</Character>
</signals>/title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications
with XML.</description>
</book>
</catalog>
I can't even understand why and I can't debug this.
Any ideas?

There are at least two things wrong here.
First, you're not saving what you thing you are.
doc = XDocument.Load(fileStream).Descendants("signals").ToList();
Here, doc is list of signals elements.
doc[0].Add(new XElement("Character", new XElement("Value", "value1"),
new XElement("Description", "This is some text")));
Here, you add a Character element to the first signals element.
doc[0].Save(sr);
And finally, you save only the first signals element. That explains why you see a signals at the start of your 'new' document.
Secondly, you're not truncating the file you're writing to. This is why you see some garbled XML after the end of your signals element - this is simply what was in the file originally, you've just overwritten the first part of it. You might want to see this question for some options to handle this. One option is just to set the length to zero before you write to it.
So, something like this:
XDocument doc;
using (var stream = await file.OpenStreamForReadAsync())
{
doc = XDocument.Load(stream);
}
var signals = doc.Descendants("signals").Single();
signals.Add(new XElement("Character", ...));
using (var stream = await file.OpenStreamForWriteAsync())
{
stream.SetLength(0);
doc.Save(stream);
}

Related

Why is xml declaration getting deleted while parsing?

I'm trying to modify some node value from one xml file to another using the below program which gets the value from the first node pub-title from a xml file in a folder called abc and then pastes the value to the first node publisher-name in another xml file in a folder named xyz.
NOTE: The escape_string method is implemented to not modify the UTF-8 entity values and keep them as they are.
var job_folders = Directory.EnumerateDirectories(textBox1.Text, "*", SearchOption.TopDirectoryOnly);
foreach (string job_folder in job_folders)
{
var target_xml_file = Directory.GetFiles(job_folder, "*.xml", SearchOption.AllDirectories).Where(a => Path.GetFileName(Path.GetDirectoryName(x)).ToLower() == "abc").First();
var target_meta_file = Directory.GetFiles(job_folder, "*.xml", SearchOption.AllDirectories).Where(a => Path.GetFileName(Path.GetDirectoryName(x)).ToLower() == "xyz").First();
string path = Path.GetFullPath(target_meta_file);
string file_content = escape_string(File.ReadAllText(path), 0);
XDocument doc = XDocument.Parse(file_content, LoadOptions.PreserveWhitespace);
var lbl=doc.Descendants("pub-title").First().Value;
XDocument doc2 = XDocument.Parse(escape_string(File.ReadAllText(target_xml_file), 0), LoadOptions.PreserveWhitespace);
doc2.DocumentType.InternalSubset = null;
doc2.Descendants("publisher-name").First().Value=lbl;
doc2.Save(target_xml_file);
File.WriteAllText(target_xml_file, escape_string(doc2.ToString(), 1));
}
MessageBox.Show("Complete");
private static string escape_string(string input_string, int option)
{
switch (option)
{
case 0:
return input_string.Replace("&", "&").ToString();
case 1:
return input_string.Replace("&", "&").ToString();
default:
return null;
}
}
Everything goes fine but <?xml version="1.0" encoding="utf-8"?> is getting deleted from the file in target_xml_file.
How do I fix this?
File before modification
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="jats-html.xsl"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD with OASIS Tables v1.0 20120330//EN" "JATS-journalpublishing-oasis-article1.dtd"[]>
<article article-type="proceedings" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:oasis="http://www.niso.org/standards/z39-96/ns/oasis-exchange/table">
<front>
<journal-meta>
<journal-id journal-id-type="publisher-id" />
<journal-title-group>
<journal-title>Eleventh & Tenth International Conference on Correlation Optics</journal-title>
</journal-title-group>
<issn pub-type="epub">0277-786X</issn>
<publisher>
<publisher-name>SPIE</publisher-name>
</publisher>
</journal-meta>
....
....
File after
<?xml-stylesheet type="text/xsl" href="jats-html.xsl"?>
<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD with OASIS Tables v1.0 20120330//EN" "JATS-journalpublishing-oasis-article1.dtd">
<article article-type="proceedings" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:oasis="http://www.niso.org/standards/z39-96/ns/oasis-exchange/table">
<front>
<journal-meta>
<journal-id journal-id-type="publisher-id" />
<journal-title-group>
<journal-title>Eleventh & Tenth International Conference on Correlation Optics</journal-title>
</journal-title-group>
<issn pub-type="epub">0277-786X</issn>
<publisher>
<publisher-name>a</publisher-name>
</publisher>
</journal-meta>
Following the answer to XDocument.ToString() drops XML Encoding Tag you should not use ToString method, use StringWriter instead:
using (var stream = new MemoryStream())
{
using (var writer = new XmlTextWriter(stream, Encoding.UTF8))
{
doc2.Save(writer);
}
string xml = escape_string(Encoding.UTF8.GetString(stream.ToArray()), 1);
File.WriteAllBytes(target_xml_file, Encoding.UTF8.GetBytes(xml));
}
Why not simply add an XDeclaration method after the process, something like
new XDeclaration("1.0", "utf-8", null)
Then save the file. It takes only two lines of code.

Windows phone 8.1 XDocument xml serialization C#

I'm creating a Windows Phone 8.1 app, and I need to serialize my data to XML.
I have two functions; the first one is creating a document where I can later put my retrieved data.
public async Task make()
{
using (var questions = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(
"data.xml",
CreationCollisionOption.OpenIfExists))
{
XDocument xml = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("Root")
);
xml.Save(questions);
}
}
The second one is making serialization to my xml file:
public async Task serial(Tsk tak)
{
using (var questions = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(
"data.xml",
CreationCollisionOption.OpenIfExists))
{
XDocument xml = XDocument.Load(questions);
xml.Root.Add(new XAttribute("Date", tak.Date),
new XElement("time", tak.Time),
new XElement("text", tak.Text)
);
xml.Save(questions);
}
}
The first xml function is making this code:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<root />
When I'm running the second function I've got this error: root element is missing. Can anyone tell me how I can get this serialization to work?
Try this, you might need to doctor it a bit:
1) create the document
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("root");
doc.AppendChild(root);
file = await ApplicationData.Current.LocalFolder.CreateFileAsync("data.xml");
await FileIO.WriteTextAsync(file, doc.GetXml());
Debug.WriteLine("Done creating file.");
2) write the new data to the document
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("data.xml");
XDocument doc = XDocument.Load(file.Path);
XElement newQuestion = new XElement("Question",
new XElement("time", tak.Time),
new XElement("text", tak.Text)
).SetAttributeValue("Date", tak.Date);
doc.Root.Add(newQuestion);
await FileIO.WriteTextAsync(file, doc.Root.ToString());

How to remove xml element in windows phone

After I could create an xml, and adding data and element to it, I want to be able to remove a specific element from it as well. I tried to follow what it said from here Deleting XML element nodes, then I could be able to remove any element from it; however, it does not remove that element completely; therefore, it's producing an error to my xml file.
My sample xml is like this (before removing)
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Favorite's xml-->
<favorites>
<favorite id="1" pro_id="1" pro_name="Boots Expert Anti-Blemish Cleansing Foam" cate_xml="ProductsOily.xml" pro_image="images/Oily-Dry/BO001.JPG" />
<favorite id="2" pro_id="2" pro_name="Clean & Clear Advantage Oil Absorbing Cream Cleanser" cate_xml="ProductsOily.xml" pro_image="images/Oily-Dry/BP251.jpg" />
</favorites>
From example, I tried to remove an element that has pro_id equals 1, but my xml file, after remove, became like this
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Favorite's xml-->
<favorites>
<favorite id="2" pro_id="2" pro_name="Clean & Clear Advantage Oil Absorbing Cream Cleanser" cate_xml="ProductsOily.xml" pro_image="images/Oily-Dry/BP251.jpg" />
</favorites>" pro_name="Clean & Clear Advantage Oil Absorbing Cream Cleanser" cate_xml="ProductsOily
Here is my code to do this:
var storage = IsolatedStorageFile.GetUserStoreForApplication();
fileName = "Favorite\\Favorite.xml";
XDocument docx = null;
using (IsolatedStorageFileStream isoStreamx = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
{
// isoStreamx.Position = 0;
docx = XDocument.Load(isoStreamx);
isoStreamx.SetLength(docx.ToString().Length);
docx.Root.Elements().Where(x => x.Attribute("pro_id").Value == NavigationContext.QueryString["id"] as string).Remove();
isoStreamx.Position = 0;
docx.Save(isoStreamx);
}
How can I completely remove an element? Please help me, thanks.
You're currently reusing the same stream to save over the top. That will only overwrite data - it won't truncate the file at the end point of your document. What you really want to do is effectively create a new file. Something like:
var storage = IsolatedStorageFile.GetUserStoreForApplication();
fileName = "Favorite\\Favorite.xml";
XDocument docx = null;
using (var isoStreamx = new IsolatedStorageFileStream(fileName, FileMode.Open, storage))
{
docx = XDocument.Load(isoStreamx);
}
var target = (string) NavigationContext.QueryString["id"];
docx.Root
.Elements()
.Where(x => x.Attribute("pro_id").Value == target)
.Remove();
using (var isoStreamx = new IsolatedStorageFileStream(fileName, FileMode.Create, storage))
{
docx.Save(isoStreamx);
}
You could keep your current code, and just call isoStreamx.SetLength(isoStreamx.Position) at the end (removing the current pointless and broken SetLength call) - but I think it's cleaner to use the code above.

XML Canonicalization returns empty elements in the transformed output

I have a related post asking how to select nodes from an XmlDocument using an XPath statement.
The only way I could get the SelectNodes to work was to create a non default namespace "x" and then explicitly reference the nodes in the XPath statement.
Whilst this works and provides me with a node list, the canonicalization then fails to produce any content to my selected nodes in the output.
I've tried using XmlDsigExcC14NTransform and specifying the namespace but this produces the same output.
Below is an example of the xml output produced (using the XML in my related post):
<Applications xmlns="http://www.myApps.co.uk/">
<Application>
<ApplicantDetails>
<Title>
</Title>
<Forename>
</Forename>
<Middlenames>
<Middlename>
</Middlename>
</Middlenames>
<PresentSurname>
</PresentSurname>
<CurrentAddress>
<Address>
<AddressLine1>
</AddressLine1>
<AddressLine2>
</AddressLine2>
<AddressTown>
</AddressTown>
<AddressCounty>
</AddressCounty>
<Postcode>
</Postcode>
<CountryCode>
</CountryCode>
</Address>
<ResidentFromGyearMonth>
</ResidentFromGyearMonth>
</CurrentAddress>
</ApplicantDetails>
</Application>
<Application>
<ApplicantDetails>
<Title>
</Title>
<Forename>
</Forename>
<Middlenames>
<Middlename>
</Middlename>
</Middlenames>
<PresentSurname>
</PresentSurname>
<CurrentAddress>
<Address>
<AddressLine1>
</AddressLine1>
<AddressLine2>
</AddressLine2>
<AddressTown>
</AddressTown>
<AddressCounty>
</AddressCounty>
<Postcode>
</Postcode>
<CountryCode>
</CountryCode>
</Address>
<ResidentFromGyearMonth>
</ResidentFromGyearMonth>
</CurrentAddress>
</ApplicantDetails>
</Application>
</Applications>
Another StackOverflow user has had a similar problem here
Playing around with this new code, I found that the results differ depending upon how you pass the nodes into the LoadInput method. Implementing the code below worked.
I'm still curious as to why it works one way and not another but will leave that for a rainy day
static void Main(string[] args)
{
string path = #"..\..\TestFiles\Test_1.xml";
if (File.Exists(path) == true)
{
XmlDocument xDoc = new XmlDocument();
xDoc.PreserveWhitespace = true;
using (FileStream fs = new FileStream(path, FileMode.Open))
{
xDoc.Load(fs);
}
//Instantiate an XmlNamespaceManager object.
System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xDoc.NameTable);
//Add the namespaces used to the XmlNamespaceManager.
xmlnsManager.AddNamespace("x", "http://www.myApps.co.uk/");
// Create a list of nodes to have the Canonical treatment
XmlNodeList nodeList = xDoc.SelectNodes("/x:ApplicationsBatch/x:Applications|/x:ApplicationsBatch/x:Applications//*", xmlnsManager);
//Initialise the stream to read the node list
MemoryStream nodeStream = new MemoryStream();
XmlWriter xw = XmlWriter.Create(nodeStream);
nodeList[0].WriteTo(xw);
xw.Flush();
nodeStream.Position = 0;
// Perform the C14N transform on the nodes in the stream
XmlDsigC14NTransform transform = new XmlDsigC14NTransform();
transform.LoadInput(nodeStream);
// use a new memory stream for output of the transformed xml
// this could be done numerous ways if you don't wish to use a memory stream
MemoryStream outputStream = (MemoryStream)transform.GetOutput(typeof(Stream));
File.WriteAllBytes(#"..\..\TestFiles\CleanTest_1.xml", outputStream.ToArray());
}
}

Add a Text line in xml on a particular location?

How can I insert the following stylesheet information into my existing xml file which is created using C#?
<?xml-stylesheet type="text/xsl" href="_fileName.xsl"?>
Or.... Can I add this line at the time of creation of the new XML file?
Edit:
I tried to achieve the above using XmlSerialier (hit and trial), something like this:
// assumes 'XML' file exists.
XmlDocument doc = new XmlDocument();
XElement dataElements = XElement.Load("_fileName.xml");
XmlSerializer xs = new XmlSerializer(typeof(Parents));
var ms = new MemoryStream();
xs.Serialize(ms, parents);
ms.Seek(0, SeekOrigin.Begin); // rewind stream to beginning
doc.Load(ms);
XmlProcessingInstruction pi;
string data = "type=\"text/xsl\" href=\"_fileName.xsl\"";
pi = doc.CreateProcessingInstruction("xml-stylesheet", data);
doc.InsertBefore(pi, doc.DocumentElement); // insert before root
doc.DocumentElement.Attributes.RemoveAll(); // remove namespaces
But the output xml is getting corrupted:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="_fileName.xsl"?>
<parents />
Whereas the desired output is something like:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="_fileName.xsl"?>
<parents>
<parent>
<Child1>
<child2>
</parent>
</parents>
Did this help to understand what's my problem???
You didn't answer the question.. "what lib do you use".
Although I advise:
XDocument
if you would use it you could do something like:
XDocument document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
document.Add(new XProcessingInstruction(
"xml-stylesheet", "type=\"text/xsl\" href=\"_fileName.xsl\""));
//and then your actual document...
document.Add(
new XElement("parent",
new XElement("child1"),
new XElement("child2")
)
);
EDIT:
Ok So you could do it like:
XDocument document = XDocument.Load("file");
document.AddFirst(new XProcessingInstruction(
"xml-stylesheet", "type=\"text/xsl\" href=\"LogStyle.xsl\""));
Is this what you're looking for?

Categories

Resources