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.
Related
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);
}
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.
When I'm trying to edit XML Element and save it, it generates copy (with edited element) and appends it to end of file.
var localStore = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream stream = new IsolatedStorageFileStream("DataFolder\\PlayerData.xml", FileMode.OpenOrCreate, FileAccess.ReadWrite, localStore);
var doc = XDocument.Load(stream);
doc.Root.Element("characters").Element("character").SetElementValue("expierence", 10);
doc.Save(stream, SaveOptions.None);
stream.Close();
Example output file:
<?xml version="1.0" encoding="utf-8"?>
<root>
<characters>
<character>
<expierence>0</expierence>
</character>
</characters>
</root><?xml version="1.0" encoding="utf-8"?>
<root>
<characters>
<character>
<expierence>10</expierence>
</character>
</characters>
</root>
That's exactly what you told it to do by passing FileMode.OpenOrCreate.
If you want to truncate any existing file, pass Create.
For more information, see the documentation.
Using some example code I found here on SO, I've put together a bit of code to open up an existing XML database, open up another XML file that's to be inserted into the first XML, get the descendants of the root node in that file, and append them to the base of the selected node in the database.xml file, and then save the resultant file to a new XML file. The code below compiles & runs with no errors in VS2010, but doesn't add the XML from the actionID.xml file to the database.xml file and I don't know what I'm missing.
Here's the C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
class pcbdbUpdate
{
static void Main( )
{
//open database & load into memory
XDocument PCBDB = XDocument.Load("C:\\database.xml");
//open Activity Log file & load into memory
XDocument addAction = XDocument.Load("C:\\actionID.xml");
//get descendant node(s) below PCBDBActivityLog root node
var actionXML = addAction.Root.Descendants("PCBDBActivityLog");
/*supposed to write out child nodes, but not very useful, just tells me
it's a: System.Xml.Linq.XContainer+<GetDescendants>d__a*/
Console.WriteLine(actionXML.ToString());
//enumerate database for SBE_PCB_Data nodes
IEnumerable<XElement> SBE_PCB_Data = PCBDB.Element("PCBDatabase").Elements("SBE_PCB_Data");
//look for specific node with PCBID of "00001" and select it
XElement newAction = SBE_PCB_Data.Where(p => p.Attribute("PCBID").Value == "00001").FirstOrDefault();
//write descendants from Activity Log to base of node
newAction.Add(actionXML);
//save the resultant file
PCBDB.Save("C:\\newDatabase.xml");
}
}
Here's the database.xml:
<?xml version="1.0" encoding="utf-8"?>
<PCBDatabase>
<SBE_PCB_Data PCBID="00001">
<Creation ActionID="0002" User="DELLIOTTG:192.168.1.69" Date="2012-10-31T14:35:58" PCBID="00001">
<PCBDrawing>00001a</PCBDrawing>
<AssemblyDrawing>00001b</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00000</PONumber>
</Creation>
<Assignment ActionID="1295" User="RHO:192.168.1.6" Date="2012-12-13T08:59:31" PCBID="00001">
<PCBDrawing>00002a</PCBDrawing>
<AssemblyDrawing>00001c</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00001</PONumber>
</Assignment>
</SBE_PCB_Data>
<SBE_PCB_Data PCBID="00002">
<Assignment ActionID="630c" User="DMUELLER:192.168.1.152" Date="2010-03-15T13:14:21" PCBID="00002">
<SBEJobNumber>57380</SBEJobNumber>
</Assignment>
</SBE_PCB_Data>
</PCBDatabase>
And here's the actionID.xml:
<?xml version="1.0"?>
<PCBDBActivityLog>
<Assignment ActionID='8353' User='DMUELLER:192.168.1.134' Date='2011-01-27T15:38:25' PCBID='00001'>
<SBEPN>41528E</SBEPN>
</Assignment>
</PCBDBActivityLog>
The resultant file should look like this:
<?xml version="1.0" encoding="utf-8"?>
<PCBDatabase>
<SBE_PCB_Data PCBID="00001">
<Creation ActionID="0002" User="DELLIOTTG:192.168.1.69" Date="2012-10-31T14:35:58" PCBID="00001">
<PCBDrawing>00001a</PCBDrawing>
<AssemblyDrawing>00001b</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00000</PONumber>
</Creation>
<Assignment ActionID="1295" User="RHO:192.168.1.6" Date="2012-12-13T08:59:31" PCBID="00001">
<PCBDrawing>00002a</PCBDrawing>
<AssemblyDrawing>00001c</AssemblyDrawing>
<Vendor>SBE</Vendor>
<PONumber>00001</PONumber>
</Assignment>
<Assignment ActionID='8353' User='DMUELLER:192.168.1.134' Date='2011-01-27T15:38:25' PCBID='00001'>
<SBEPN>41528E</SBEPN>
</Assignment>
</SBE_PCB_Data>
<SBE_PCB_Data PCBID="00002">
<Assignment ActionID="630c" User="DMUELLER:192.168.1.152" Date="2010-03-15T13:14:21" PCBID="00002">
<SBEJobNumber>57380</SBEJobNumber>
</Assignment>
</SBE_PCB_Data>
</PCBDatabase>
What am I missing or doing wrong?
Error is here:
var actionXML = addAction.Root.Descendants("PCBDBActivityLog")`
You are trying to find <PCBDBActivityLog> elements under the document root. But this will return nothing, because <PCBDBActivityLog> element is a root of actionID.xml document. Replace this line with
var actionXML = addAction.Descendants("PCBDBActivityLog");
Or
var actionXML = addAction.Root;
I am writing an application where i need to pull information out of a XML Document.
My XML document is stored in my projects bin/ Debug file.
I cant get it working.
XML document named informationData:
<xml>
<information>
<name >stian</name>
<surname>Kruger</surname>
<tel>0825514302</tel>
<photo>1234JLJ.jpg</photo>
</information>
</xml>
my call code:
private void btnReadXML_Click(object sender, EventArgs e)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("informationData.xml");
XmlNodeList dataNodes = xmlDoc.SelectNodes("/information");
foreach (XmlNode node in dataNodes)
{
Name = node.SelectSingleNode("name").InnerText;
Surname = node.SelectSingleNode("surname").InnerText;
TelNumber = Convert.ToInt32(node.SelectSingleNode("tel").InnerText);
}
}
Your XPath selector is wrong. Replace:
XmlNodeList dataNodes = xmlDoc.SelectNodes("/information");
with:
XmlNodeList dataNodes = xmlDoc.SelectNodes("//information");
or with:
XmlNodeList dataNodes = xmlDoc.DocumentElement.SelectNodes("information");
Also make sure that the XML file is present in the same folder as the running executable (you said bin/Debug/informationData.xml). If the XML file is part of your Visual Studio project you could select it and in the properties set Copy to Output Directory to Copy if newer. This way VS will automatically copy the XML file to this output folder everytime you compile the project.
You can use this code
<?xml version="1.0" encoding="utf-8" ?>
<information>
<name >stian</name>
<surname>Kruger</surname>
<tel>0825514302</tel>
<photo>1234JLJ.jpg</photo>
</information>
var xmlDoc = XDocument.Load("informationData.xml");
var name = xmlDoc.Element("name").Value;
var surname = xmlDoc.Element("surname").Value;
var telNumber = Convert.ToInt32(xmlDoc.Element("tel").Value);
add <?xml version="1.0" encoding="utf-8"?> as first line in XML file