Save back to XML file with changed element - c#

I have my XML as:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>`
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std_Ctrl.flg</VALUE>
<VALUE STEP="2">Sas_Card.flg</VALUE>
<VALUE STEP="-1">eSAS1.flg</VALUE>
</INDEX>
<INDEX>
<VALUE>0</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Got the Values using LINQ and displayed all elements in Textbox, only VALUE elements.
var hdd= from n1 in x.Elements("DECLARATION") where 1.Attribute("NAME").Value.Trim() == "1ST_HDD_SATA_Position" select n1.Elements("INSTANCE").Elements("PROPERTY").Elements("INDEX").Elements("VALUE");
Then Updated the textbox to include new values for VALUE Element. For example lets say I updated it to some string.
foreach (IEnumerable<XElement> elList in hdd)
foreach (XElement el in elList)
{
el.ReplaceNodes("string");
el.Save(m);
}
Isn't this suppose to save my xml file with the update?

There are many problems with your code, starting with the fact that you posted invalid XML. Assuming that your XML was like this:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Then the following code works:
var doc = XDocument.Load("Configuration.xml");
var hdd = from n1 in doc.Elements("CONFIGURATION").Elements("DECLARATION")
let nameAttribute = n1.Attribute("NAME")
where nameAttribute != null && nameAttribute.Value.Trim() == "1ST_HDD_SATA"
select n1.Elements("INSTANCE")
.Elements("PROPERTY")
.Elements("INDEX")
.Elements("VALUE");
foreach (var elList in hdd)
foreach (var el in elList)
{
el.ReplaceNodes("string");
}
doc.Save("Configuration.xml");

Related

Deserialize key/value XML data to C# object

I need deserialize XML file "c:\Temp\Des.xml":
<return xsi:type="ns2:Map"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<item>
<key xsi:type="xsd:int">218980</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">218980</value>
</item>
<item>
<key xsi:type="xsd:string">title</key>
<value xsi:type="xsd:string">Product Title 1</value>
</item>
<item>
<key xsi:type="xsd:string">price</key>
<value xsi:type="xsd:string">10.30</value>
</item>
<item>
<key xsi:type="xsd:string">images</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image1.jpg</value>
</item>
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image2.jpg</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:string">specifications</key>
<value SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name1</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value1</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name2</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value2</value>
</item>
</item>
</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:int">218981</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">218981</value>
</item>
<item>
<key xsi:type="xsd:string">title</key>
<value xsi:type="xsd:string">Product Title 2</value>
</item>
<item>
<key xsi:type="xsd:string">price</key>
<value xsi:type="xsd:string">10.40</value>
</item>
<item>
<key xsi:type="xsd:string">images</key>
<value xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image4.jpg</value>
</item>
<item>
<key xsi:type="xsd:string">231d314ae3f1df4d56bf267fb194c537</key>
<value xsi:type="xsd:string">https://test.com/Image5.jpg</value>
</item>
</value>
</item>
<item>
<key xsi:type="xsd:string">specifications</key>
<value SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name12</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value12</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">name22</value>
</item>
<item>
<key xsi:type="xsd:string">value</key>
<value xsi:type="xsd:string">value22</value>
</item>
</item>
</value>
</item>
</value>
</item>
</return>
I created class for that
[Serializable()]
[XmlRoot("Item", Namespace = "", IsNullable = false)]
public class Item
{
[XmlElement("id")]
public int Id { get; set; }
[XmlElement("title")]
public string Title { get; set; }
[XmlElement("price")]
public decimal Price { get; set; }
[XmlElement("images")]
public string[] Images { get; set; }
[XmlElement("specifications")]
public object Specifications { get; set; }
}
and call Deserialize:
public class Program
{
static void Main(string[] args)
{
string xml = File.ReadAllText("c:\\Temp\\Des.xml");
StringReader stringReader = new StringReader(xml);
XmlSerializer serializer = new XmlSerializer(typeof(List<Item>), new XmlRootAttribute("return"));
List<Item> items = (List<Item>) serializer.Deserialize(stringReader);
but items are always empty list, what I'm doing wrong? Thank you!
As #jle said here:
I don't think attributes will work because of the key/value structure.
There is no way for a program to infer from the XML alone what
properties an object has. I would make a static extension method
helper function to get the values:
you can use the below extension method for getting the value of each key:
public static class XmlHelper
{
public static string GetValueByKeyName(this XElement element, string key)
{
return element.Descendants("key")
.First(v => v.Value == key)
.ElementsAfterSelf("value")
.First()
.Value;
}
}
and then, your code became like that:
var ItemObjects = new List<Item>();
XDocument doc = XDocument.Load(#"D:/file.xml");
var elements = doc.Root.Elements("item").ToList();
elements.ForEach(item => {
ItemObjects.Add(new Item {
Id = int.Parse(item.GetValueByKeyName("id")),
Title = item.GetValueByKeyName("title"),
Price = decimal.Parse(item.GetValueByKeyName("price"), CultureInfo.InvariantCulture),
//other props
});
});
I test it with your data, and it works properly. good luck.
Ok, I found solution, in case anyone need it, I use Linq to XML for that:
var items = (from element in xdoc.Descendants("return").Elements("item")
select new
{
Id = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "id").Elements("value").First().Value,
Title = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "title").Elements("value").First().Value,
Price = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "price").Elements("value").First().Value,
Images = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "images").Elements("value").Elements("item").Elements("value").Select(i => i.Value).ToList(),
Specifications = element.Elements("value").Elements("item").Where(c => c.Element("key").Value == "specifications").Elements("value").Elements("item").Select(s =>
new KeyValuePair<string, string>
(
s.Elements("item").First().Elements("value").First().Value,
s.Elements("item").First().Elements("value").Last().Value
)
).ToList(),
}
).ToList();

How do I edit the value of a specific node in XML when there are multiple nodes with the same name?

I am trying to edit a specific node in an XML document. I have the following XML data:
<Items xmlns="http://foo.com/blah/blah">
<Item>
<Format>1</Format>
<Name>Edit this one</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
<Item>
<Format>1</Format>
<Name>Edit this one</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
</Items>
I want to append a number to each Item/Name node content but not to the Metadata/Name nodes, save the file off as test_n.xml and repeat n number of times.
The code I'm using seems to get me what I want for the Item/Name node and saved the file(s) correctly but it also updates the Metadata/Name nodes and I do not want that value to be updated. I understand the problem is in the navigator.Select call but I just don't know how to update one and skip the other.
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
XPathNavigator navigator = doc.CreateNavigator();
XmlNamespaceManager manager = new XmlNamespaceManager(navigator.NameTable);
manager.AddNamespace("at", "http://foo.com/blah/blah");
for (int i = 0; i < 10; i++)
{
foreach (XPathNavigator nav in navigator.Select("//at:Name", manager))
{
var currValue = nav.Value;
nav.SetValue(currValue + " " + i);
}
doc.Save("test_" + i + ".xml");
}
In the end I'd like it to be:
<Items xmlns="http://foo.com/blah/blah">
<Item>
<Format>1</Format>
<Name>Edit this one 0</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
<Item>
<Format>1</Format>
<Name>Edit this one 0</Name>
<MetaDataDictionary>
<MetaData>
<Name>Do not edit this one</Name>
<Value>0</Value>
</MetaData>
</MetaDataDictionary>
</Item>
</Items>
Don't select all name tags, instead select only name tags which are children of /Items/Item tag.
The XPath query would look like /Items/Item/Name.
You can test it here. See the documentation for details.

Parsing Soap Message

I recently started to work with SOAP.
Right now I am trying to parse SOAP message in C#.
Message is as it follows:
<SOAP-ENV:Body xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<ns1:getBuildingsResponse xmlns:ns1="http://someserver.net/~username/lab/servis?ws=1">
<return SOAP-ENC:arrayType="ns2:Map[2]" xsi:type="SOAP-ENC:Array" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">1</value>
</item>
<item>
<key xsi:type="xsd:string">code</key>
<value xsi:type="xsd:string">345-GESG</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">Building 1</value>
</item>
</item>
<item xsi:type="ns2:Map">
<item>
<key xsi:type="xsd:string">id</key>
<value xsi:type="xsd:string">7590913</value>
</item>
<item>
<key xsi:type="xsd:string">code</key>
<value xsi:type="xsd:string">353-gr</value>
</item>
<item>
<key xsi:type="xsd:string">name</key>
<value xsi:type="xsd:string">Building 2</value>
</item>
</item>
</return>
I want to extract values of keys id,code and name.
I tried doing something like this:
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(client.Invoke("getBuildings").ToString());
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
nsmgr.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
nsmgr.AddNamespace("i", "item");
XmlNodeList xNodelst = xdoc.DocumentElement.SelectNodes("item", nsmgr);
Console.WriteLine(xNodelst.Count);
foreach (XmlNode xn in xNodelst)
{
Console.WriteLine(xn.Value);
}
The problem is, I don't know how to act with tags that have no namespace...
This line of code:
Console.WriteLine(xNodelst.Count);
always prints 0, but I want it to print 2, since I have 2 elements in array (ns2:Map[2]).
Meaning, I want to loop through all of these elements:
<item xsi:type="ns2:Map">
Any help will be appreciated.
XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(client.Invoke("getBuildings").ToString());
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
XmlNodeList nodes = xDoc.SelectNodes("//item[#xsi:type='ns2:Map']",nsmgr);
var nodeCount=nodes.Count;
try this, this might help you.

How to add xml text in existing xml file if my xml having same node twice

i have following xml file :
<?xml version="1.0" encoding="UTF-8"?>
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<DataSets>
<DataSet Name="Data">
<Query>
<DataSourceName>DS1</DataSourceName>
<CommandText>text</CommandText>
</Query>
</DataSet>
<DataSet Name="table22">
<Query>
<DataSourceName>DS1</DataSourceName>
<CommandText>New text2</CommandText>
</Query>
i have to add following xml text after ending of first "Query" element using Xelement and XAttribute
my first xml text to add is:
<Field Name="CommunicationDataValueId">
<DataField>CommunicationDataValueId</DataField>
<TypeName>System.Int64</TypeName>
</Field>
<Field Name="DeviceMasterId">
<DataField>DeviceMasterId</DataField>
<TypeName>System.Int32</TypeName>
</Field>
my second xml text to add after ending of second query element :
<Field Name="Min">
<DataField>Min</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
<Field Name="Max">
<DataField>Max</DataField>
<rd:TypeName>System.Int64</rd:TypeName>
</Field>
How to do it???
Expected output should be:
<?xml version="1.0" encoding="UTF-8"?>
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition" xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<DataSets>
<DataSet Name="Data">
<Query>
<DataSourceName>DS1</DataSourceName>
<CommandText>text</CommandText>
</Query>
<Field Name="CommunicationDataValueId">
<DataField>CommunicationDataValueId</DataField>
<TypeName>System.Int64</TypeName>
</Field>
<Field Name="DeviceMasterId">
<DataField>DeviceMasterId</DataField>
<TypeName>System.Int32</TypeName>
</Field>
</DataSet>
<DataSet Name="table22">
<Query>
<DataSourceName>DS1</DataSourceName>
<CommandText>New text2</CommandText>
</Query>
<Field Name="Min">
<DataField>Min</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
<Field Name="Max">
<DataField>Max</DataField>
<rd:TypeName>System.Int64</rd:TypeName>
</Field>
can anyone help to solve this please!!!!!!!
By using XDocument
var doc = XDocument.Parse("your xml string");
//namespace
var nspace = doc.Root.Name.Namespace;
foreach (var item in doc.Descendants(nspace+"DataSet"))
{
var str = new StringBuilder();
var str1 = new StringBuilder();
str.Append("<Field Name='CommunicationDataValueId'>");
str.Append("<DataField>CommunicationDataValueId</DataField>");
str.Append("<TypeName>System.Int64</TypeName>");
str.Append("</Field>");
str1.Append("<Field Name='DeviceMasterId'>");
str1.Append("<DataField>DeviceMasterId</DataField>");
str1.Append("<TypeName>System.Int32</TypeName>");
str1.Append("</Field>");
item.Add(XElement.Parse(str.ToString()));
item.Add(XElement.Parse(str1.ToString()));
}
Take a look at the following API for XElement - AddAfterSelf.
You can find each relevant Query element and call that method, adding the piece of XML you want to add.

Parsing xml attribute value from specified node area

I have an XML which follow the following structure:
<model uir="model1">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
<model uir="model2">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
This that i want is to take the value of the attribute double and to store it in a list (or vector) for each model. When the node value hasn't an attribute double to insert a null. In first level i tried the following but when it reaches in a node with no attribute double stack.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
class MyClass
{
static void Main(string[] args)
{
string file = #"C:\RDMS.xml";
XDocment doc = XDocument.Load(file);
foreach (var item in doc.Descendants("value"))
{
Console.WriteLine(item.Attribute("double").Value);
}
Console.ReadKey();
}
}
You did not specify the error, but I am guessing you are getting a NullReferenceException? You need to check if item.Attribute actually returns a value.
var doubleAttr = item.Attribute("double");
if(doubleAttr == null) continue;
Console.WriteLine(item.Attribute("double").Value);
If that is not your problem, then please be more specific as to what your problem is.
Further clarification from trope's comment below:
...you are getting a NullReferenceException because nothing returns from item.Attribute("double") for the elements that lack that attribute, therefore you cannot then call .Value, as item.Attribute("double") is null, and you receive a NullReferenceException. This does not happen with your index attribute, because all of your "value" elements have index attributes...
You can check the result of Attribute(string) if it is null:
var attrib = item.Attribute("double");
if (attrib != null)
Console.WriteLine(attrib.Value);
else
Console.WriteLine("attribute not found!");

Categories

Resources