I have generated an xml by using DataTable.WriteXML() method, which converts my table fields into elements of an xml file - that's precisely what I need.
Now I want to add another element and make an existing element as its child.
Existing:
<DocketImport>
<Docket>
<XRefCode>1578</XRefCode>
<Name>1578</Name>
<PieceRate>0</PieceRate>
<OrgXRefCode>terminalA</OrgXRefCode>
</Docket>
</DocketImport>
Desired:
<DocketImport>
<Docket>
<XRefCode>1578</XRefCode>
<Name>1578</Name>
<PieceRate>0</PieceRate>
<Org>
<OrgXRefCode>terminalA</OrgXRefCode>
</Org>
</Docket>
</DocketImport>
In case following result would be acceptable:
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<DataTable1>
<XRefCode>1578</XRefCode>
<Name>1578</Name>
<PieceRate>0</PieceRate>
<Org xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OrgXRefCode>terminalA</OrgXRefCode>
</Org>
</DataTable1>
</DocumentElement>
You can achieve desired behavior by defining custom type for last columns of your DataTable
public class Org
{
public string OrgXRefCode { get; set; }
}
Than your DataTable initialization could look as following:
var dataTable = new DataTable("DataTable2");
dataTable.Columns.AddRange(new DataColumn[]{
new DataColumn("XRefCode"),
new DataColumn("Name"),
new DataColumn("PieceRate"),
new DataColumn("Org", typeof(Org))
});
var dataRow = dataTable.NewRow();
dataRow["XRefCode"] = 1578;
dataRow["Name"] = 1578;
dataRow["PieceRate"] = 0;
dataRow["Org"] = new Org()
{
OrgXRefCode = "terminalA"
};
dataTable.Rows.Add(dataRow);
calling dataTable.WriteXml() will produce your expected XML
Related
this is my code to fetch xml values from files and it does successfully but single element like an in file type but when i try to fetch array of records from xml it failed
public void readXmlFiles()
{
var xml = XDocument.Load(#"C:\Applications\Files\Xmldemo.xml");
var format = from r in xml.Descendants("insureance")
select new
{
type = r.Element("type").Value,
// values=r.Element("value").Value
};
foreach (var r in format)
{
Console.WriteLine(r.values);
}
}
this is my xml file
<?xml version="1.0" encoding="UTF-8"?>
<insureance>
<type>Risk</type>
<classofbusiness type="array">
<value>0</value>
<value>1</value>
<value>2</value>
</classofbusiness>
</insureance>
now i want to fetch classofbusiness all values thanx in advance
Your current attempt selects a single value element, which doesn't exist as a child of insureance. I would guess you get a null reference exception as a result.
You need to follow the structure of the document
var format =
from ins in doc.Descendants("insureance")
select new
{
Type = (string) ins.Element("type"),
ClassOfBusiness = ins.Elements("classofbusiness")
.Elements("value")
.Select(x => (int) x)
.ToArray()
};
var format = xml.Elements("insureance").Elements("classofbusiness").Descendants();
I have an xml file like this:
<xml version="1.0" encoding="UTF-8">
<configes>
<Username>Administrator</Username>
<Password>123456</Password>
<Domain>sample</Domain>
<Url>http://sample/Organization.svc</Url>
</configes>
I loaded it into a datagridview by something like this:
DataGridViewRow row;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(txtXmlAdd.Text);
XmlElement elm = xmlDoc.DocumentElement;
XmlNodeList nodeList = elm.ChildNodes;
foreach (XmlNode node in nodeList)
{
row = (DataGridViewRow)dgvXML.Rows[0].Clone();
row.Cells[0].Value = node.Name ;
row.Cells[1].Value = node.InnerText;
dgvXML.Rows.Add(row);
}
Now, I want to change some of attributes(edit value, or add new attribute) and save it to the same file again.
I try this but it doesn't work:
DataTable dT = GetDataTableFromDGV(dgvXML);
DataSet dS = new DataSet();
dS.DataSetName = "configes";
dS.Tables.Add(dT);
dS.WriteXml(string path);
when GetDataTableFromDGV is something like this:
var dt = new DataTable();
dt.TableName="";
string firstCol = "";
string secCol = "";
object[] cellValues = new object[1];
foreach (DataGridViewRow row in dgv.Rows)
{
secCol = row.Cells[1].Value.ToString();
firstCol = row.Cells[0].Value.ToString();
dt.Columns.Add(firstCol);
cellValues[0] = row.Cells[1].Value;
dt.Rows.Add(cellValues);
}
return dt;
Do it simple.
Define form field (not local variable):
DataSet dataSet = new DataSet();
Load data from the file:
dataSet.ReadXml(filename);
Bind DataTable to DataGridView:
dataGridView.DataSource = dataSet.Tables[0];
The data will be displayed in DataGridView. Now you can edit them.
Save data to the file:
dataSet.Tables[0].WriteXml(filename);
If you want show each column and its value in a row then you have two variants.
Change the xml file format as follows:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<configes>
<Name>UserName</Name>
<Value>Administrator</Value>
</configes>
<configes>
<Name>Password</Name>
<Value>123456</Value>
</configes>
<configes>
<Name>Domain</Name>
<Value>sample</Value>
</configes>
<configes>
<Name>Url</Name>
<Value>http://sample/Organization.svc</Value>
</configes>
</Root>
In that case you can use short and clean C#-code above.
If you can't change the format of the xml file, then you have to parse it manually and put the values in the DataTable.
As I told you in my comment, the solution to your problem is in an old answer here: Editing xml on live in C#. Deleting nodes that contain specific value
But if you want I can rewrite this for you ....
Assuming you have your xml file stored in a dataTable, if you edit the data in this dataTable you can rewrite your xml file like the first one.
//READ THE XML FILE
XmlDocument xmlDoc = new XmlDocument();
//My path
xmlDoc.LoadXml(Properties.Resources.test);
//Read the xml file into a dataSet
DataSet ds = new DataSet();
XmlNodeReader xnr = new XmlNodeReader(xmlDoc);
ds.ReadXml(xnr);
//Your data will be store in the 4's dataTable of the dataSet ( the <field> )
for (int i = 0; i < ds.Tables[0].Columns.Count; i++)
{
//RemoteAt will remove all the node, so the node <Field> in your example data
ds.Tables[0].Rows[0][i] = "NEWVALUE";
//If you want to only remove the node <Value> (and not all the <Field> node ) just do ds.Tables[4].Rows["Value"]=null;
}
//Write your new content in a new xml file
//As you wanted here you just read the new xml file created as a string
using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
ds.WriteXml(xmlTextWriter);
xmlTextWriter.Flush();
stringWriter.GetStringBuilder().ToString();
//Here the result is in stringWriter, and there is 6 <Field> nodes, and not 8 like before the suppress
}
//If you want to create a new xml file with the new content just do
ds.WriteXml(pathOfTheFirstXmlFile);
//( like rewriting the previous xml file )
My input: ( test.xml )
<?xml version="1.0" encoding="UTF-8"?>
<configes>
<Username>Administrator</Username>
<Password>123456</Password>
<Domain>sample</Domain>
<Url>http://sample/Organization.svc</Url>
</configes>
My output after the previous code:
<?xml version="1.0" standalone="yes"?>
<configes>
<Username>NEWVALUE</Username>
<Password>NEWVALUE</Password>
<Domain>NEWVALUE</Domain>
<Url>NEWVALUE</Url>
</configes>
I have to generate (serialize to) XML from object with array of (Order) elements.
Order class generated from XSD has sequence attribute:
[System.Xml.Serialization.XmlAttributeAttribute(DataType = "token")]
public string Sequence;
I am using .Net XMLSerializer, but it does not generate automagically for every Order element Sequence attribute.
Having:
Order[] Orders = new Order[2] {...}
I have to get:
<Order Sequence="1">..</Order>
<Order Sequence="2">..</Order>
And for just one, single element it should render:
<Order Sequence="1">..</Order>
Does anyone know how to make XMLSerialzier renders this attribute automagically? Or do I need to manually set Sequence for every Order element?
Cheers
AFAIK there is no way to achieve this with out-of-the-box methods. This leaves you with the following options:
IXmlSerializable
Implement IXmlSerializable on the object that contains the array of Orders. This way, you can either serialize the Orders manually or set the sequence number of the Order before serializing the object into the XmlWriter using the XmlSerializer.Serialize method:
public class OrdersContainer : IXmlSerializable
{
public Order[] Orders;
public void WriteXml(XmlWriter writer)
{
// Serialize other properties
writer.WriteStartElement("Orders");
var ser = new XmlSerializer(typeof(Order));
for(var i = 0; i < Orders.Length; i++)
{
Orders[i].Sequence = (i + 1).ToString();
ser.Serialize(writer, Orders[i]);
}
writer.WriteEndElement(); // Orders
}
// ...
}
This generated the following XML:
<?xml version="1.0" encoding="utf-16"?>
<OrdersContainer>
<Orders>
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Sequence="1">
<Name>Order 1</Name>
</Order>
<Order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Sequence="2">
<Name>Order 2</Name>
</Order>
</Orders>
</OrdersContainer>
The XmlSerializer places some namespace declarations on the Order elements, but that doesn't hurt. My sample class had a Name property on the Order class.
The downside of this approach is that you have to implement the serialization of the OrdersContainer class manually, including the deserialization.
Linq to XML
The second option is to use Linq to XML for the serialization part. You'd also have to implement the serialization manually, but you could use the otb XmlSerializer for deserialization (on the other hand you might want to avoid to mix two different frameworks). You could serialize the OrdersContainer class as follows and also write the sequence number:
var doc = new XDocument(new XElement("OrdersContainer",
new XElement("Orders",
cont.Orders.Select((x, i) => new XElement("Order",
new XAttribute("Sequence", (i + 1).ToString()),
new XElement("Name", x.Name))))));
doc.Save(writer);
This created the following XML:
<?xml version="1.0" encoding="utf-16"?>
<OrdersContainer>
<Orders>
<Order Sequence="1">
<Name>Order 1</Name>
</Order>
<Order Sequence="2">
<Name>Order 2</Name>
</Order>
</Orders>
</OrdersContainer>
Please note that the sample uses an overload of the Select method that also receives the index of the item so that the sequence number can be created based upon the position in the array.
If order is a class of yours, you can add a property with [XmlAttributeAttribute]
class Order {
[XmlAttribute()]
public int Sequence { get; set; }
But you should set this Sequence property in all items of your list.
this works fine -
var o = new XmlSerializerNamespaces();
o.Add("", "");
var ser = new XmlSerializer(typeof(List<Order>), "");
using (var sw = new StringWriter())
{
ser.Serialize(sw, new List<Order> {
new Order { sequence = "1", MyProperty = 1 },
new Order { sequence = "2", MyProperty = 2 } },
o);
var t = sw.ToString();
}
AND
[XmlAttribute(AttributeName = "sequence", DataType = "string")]
public string sequence { get; set; }
I have an string parameter with xml content in it. Basically the string have an XML inside.
string S = funcThatReturnsXML (parameters);
S have the next text:
<?xml version="1.0" encoding="utf-8" ?>
<tagA>
<tagB>
<tagBB>
..
.
.
</tagBB>
.
.
</tagB>
<tagC>
..
..
.
</tagC>
</tagA>
The funcThatReturnsXML (parameters) creates an XmlDocument object but the return it as a string, I cant change this function, to much stuff works with it.
Tried to create XmlDocument objetc but the SelectSingleNode return null.
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
XmlNode root = xmlDoc.SelectSingleNode("tagB");
How can I delete from string S (not XML Object) specific node, for example <tagB>
EDIT: this is the XML I tested with:
<?xml version="1.0" ?>
- <Request xmlns:xsi="http://www.mysite.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <info xmlns="http://www.mysite.com">
<RequestTR>54</RequestTR>
<time>2013-12-22</time>
</info>
- <Parameters xmlns="http://www.mysite.com">
<id>3</id>
<name>2</name>
</Parameters>
<title>Request</title>
</Request>
Try this:
string S = funcThatReturnsXML(parameters);
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants("tagB");
nodeToRemove.Remove();
That will remove all nodes named "tagB" from string S which contains xml.
UPDATE 1:
Sorry, i missed to include one more line:
S = doc.ToString();
My first code above removed "tagB" from doc but didnt save it back to S variable.
UPDATE 2:
I tested with following xml which contain attribute:
<tagA attribute="value">
<tagB>
<tagBB>
</tagBB>
</tagB>
<tagC></tagC>
</tagA>
and the output of Console.WriteLine(S):
<tagA attribute="value">
<tagC></tagC>
</tagA>
UPDATE 3:
Given your updated xml format, I know why my previous code didn't work for you. That was because your xml have namespace (xmlns) declared. The solution is to use LocalName when searching for the node to be removed, that will search for node name while ignoring its namespace. The follwoing example shows how to remove all "info" node:
var doc = XDocument.Parse(S);
var nodeToRemove = doc.Descendants().Where(o => o.Name.LocalName == "info");
nodeToRemove.Remove();
S = doc.ToString();
If you can determine the particular outer element to remove from the returned XML, you could use LINQ to XML:
var returnedXml = funcThatReturnsXML(parameters);
var xmlElementToRemove = funcThatReturnsOuterElement(returnedXml);
var xelement = XElement.Load("XmlDoc.txt");
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
For example:
using System.Linq;
using System.Xml.Linq;
class Program
{
static void Main(string[] args)
{
// pretend this is the funThatReturnsXML return value
var returnedXml = "<tagB><tagBB></tagBB></tagB>";
// get the outer XML element name
var xmlElementToRemove = GetOuterXmlElement(returnedXml);
// load XML from where ever
var xelement = XElement.Load("XmlDoc.txt");
// remove the outer element and all subsequent elements
xelement.Elements().Where(e => e.Name == xmlElementToRemove).Remove();
}
static string GetOuterXmlElement(string xml)
{
var index = xml.IndexOf('>');
return xml.Substring(1, index - 1);
}
}
Note that the above is a "greedy" removal method, if there is more than once element with the name returned via the GetOuterXmlElemet method they will all be removed. If you want a specific instance to be removed then you will require something more sophisticated.
Building on your edit:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(S);
var nodeA = xmlDoc.SelectSingleNode("/tagA");
var nodeB = nodeA.SelectSingleNode("tagB");
nodeA.RemoveChild(nodeB);
To remove (possibly) multiple tagB nodes in unknown positions, you may try:
var bees = xmlDoc.SelectNodes("//tagB");
foreach (XmlNode bee in bees) {
var parent = bee.ParentNode;
parent.RemoveChild(bee);
}
i should to parse an XML file in my C# application.
This is a sample of my XML file
<?xml version="1.0" encoding="utf-8" ?>
<tokens>
<token id="1" >
<id>1</id>
<nome>"Pippo"</nome>
<numeroSillabe>"2"</numeroSillabe>
<sillabaIniziale>"Pip"</sillabaIniziale>
</token>
<token id="2">
<id>2</id>
<nome>Pluto</nome>
<numeroSillabe>2</numeroSillabe>
<sillabaIniziale>Plu</sillabaIniziale>
</token>
<token id="3">
<id>3</id>
<nome>Palla</nome>
<numeroSillabe>2</numeroSillabe>
<sillabaIniziale>Pa</sillabaIniziale>
</token>
</tokens>
For reading this file i use this code
XmlDocument document = new XMLDocument()
document.Load("Content\\Token.xml");
// arrivo al nodo
//XmlNode node = document.DocumentElement.SelectSingleNode("/tokens/token");
//dichiaro la mappa per i token
Dictionary<string, Token> mappa = new Dictionary<string, Token>();
foreach (XmlNode node in document.DocumentElement.ChildNodes)
{
//creo l'oggetto Token e lo popolo con i risultati del parse
Token token = new Token();
//compilo il campo ID
token.setId((node["id"].InnerText).Replace("\"", ""));
//compilo il nome
token.setNome((node["nome"].InnerText).Replace("\"", ""));
//compilo l numero sillabe
token.setNumeroSillabe((node["numeroSillabe"].InnerText).Replace("\"", ""));
//compilo la sillaba iniziale
token.setSillabaIniziale(node["sillabaIniziale"].InnerText);
//aggiungo all array il token
mappa.Add(token.getId(), token);
}
It works.
Now i would like to search the element from XML document for example SELECT * FROM DOCUMENT_XML WHERE numeroSilabe=2.
How can I perform this task?
If you need to navigate through your XML file, like in your code sample, you could go with something like this:
var mappa = document.SelectNodes("/tokens/token[numeroSillabe=2]") // Filter
.OfType<XmlElement>()
.ToDictionary(
t => t.GetAttribute("id"),
t => new
{
Id = t.GetAttribute("id"),
Nome = t.SelectSingleNode("nome" ).InnerText,
NumeroSillabe = t.SelectSingleNode("numeroSillabe" ).InnerText,
SillabaIniziale = t.SelectSingleNode("sillabaIniziale").InnerText
});
List<Token> tokens = mappa.Select(x=>x.Values.Where(y=>y.getNumeroSillabe()==2)).ToList();
I suppose that your token class has a method getNumeroSillabe() like getId(), which returns the value of NumeroSillabe.
By the way, I would suggest you to use automatic properties instead of defining foreach variable of your class a getter and a setter. Certainly, that would be suitable, if you don't have any logic in this methods instead of setting and getting the value of a variable, without checking anything or doing any calculation etc.
In your case:
// I don't know, which is the access modifier your class and I suppose that is public.
public class Token
{
public string Id { get; set;}
public string Nome { get; set;}
public string NumeroSillabe { get; set;}
public string SillabeIniziale { get; set;}
// here goes the methods of your class, if there are any
}
making this change, the code for the above linq query changes to the following:
List<Token> tokens = mappa.Select(x=>x.Values.Where(y=>y.NumeroSillabe==2)).ToList();
Also your code changes for instance as follows:
token.Nome = node["nome"].InnerText.Replace("\"", "");
The same holds for the other three assignments.
For further reading regrading the auto implemented properties, please look in the following link:
http://msdn.microsoft.com/en-us/library/vstudio/bb384054.aspx
use this:
var mappa = document.SelectNodes("//token[numeroSillabe=2]");
Try below code to get filtered nodes without any loop:
var document = new XmlDocument();
document.Load("Content\\Token.xml");
var filteredNodes = document.SelectNodes("tokens/token/numeroSillabe=2");