I have an XML file like this:
<caseData>
<entity type="case" name="1">
<attribute name="CASE_OPEN" value="false"/>
<attribute name="CASE_NUMBER" value=""/>
<attribute name="CASE_TYPE" value=""/>
</entity>
<caseData>
I need to update the value for the CASE_NUMBER and CASE_TYPE. The way I only can do is:
_xd = new XmlDocument();
_xd.LoadXml(xmlTemplate);
var caseitem = _xd.GetElementsByTagName("entity")[0];
var childnodes = caseitem.ChildNodes;
foreach (XmlNode node in childnodes)
{
if (node.Attributes["name"].Value == "CASE_NUMBER")
{
node.Attributes["value"].Value = "11222";
}
if (node.Attributes["name"].Value == "CASE_TYPE")
{
node.Attributes["value"].Value = "NEW";
}
}
I am wondering if there is a better way to do it.
Thanks!
Another option is using LINQ to XML. It's generally a nicer API to work with:
var doc = XDocument.Parse(xmlTemplate);
var caseNumber = doc
.Descendants("attribute")
.Single(e => (string)e.Attribute("name") == "CASE_NUMBER");
caseNumber.SetAttributeValue("value", "11222");
If this really is a template and you're just filling in the blanks, you can pretty easily just create it from scratch:
var attributes = new Dictionary<string, string>
{
{"CASE_OPEN", "false"},
{"CASE_NUMBER", "11122"},
{"CASE_TYPE", "NEW"}
};
var caseData = new XElement("caseData",
new XElement("entity",
new XAttribute("type", "case"),
new XAttribute("name", "1"),
AttributeElements(attributes)
)
);
Where AttributeElements is something like:
private static IEnumerable<XElement> AttributeElements(
IReadOnlyDictionary<string, string> attributes)
{
return attributes.Select(x => new XElement("attribute",
new XAttribute("name", x.Key),
new XAttribute("value", x.Value)
));
}
Related
I've got this XML:
<?xml version="1.0" encoding="utf-8"?>
<JMF SenderID="InkZone-Controller" Version="1.2">
<Command ID="cmd.00695" Type="Resource">
<ResourceCmdParams ResourceName="InkZoneProfile" JobID="K_41">
<InkZoneProfile ID="r0013" Class="Parameter" Locked="false" Status="Available" PartIDKeys="SignatureName SheetName Side Separation" DescriptiveName="Schieberwerte von DI" ZoneWidth="32">
<InkZoneProfile SignatureName="SIG1">
<InkZoneProfile Locked="False" SheetName="S1">
<InkZoneProfile Side="Front">
<ColorPool Class="Parameter" DescriptiveName="Colors for the job" Status="Available">
<InkZoneProfile Separation="PANTONE 647 C" ZoneSettingsX="0 0,003 " />
</ColorPool>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</InkZoneProfile>
</ResourceCmdParams>
</Command>
</JMF>
I'm trying to add a node after a specific node() , using XElement and Linq. But my LINQ query always returns me null.
Tried this:
XElement InkZonePath = XmlDoc.Element("JMF").Elements("InkZoneProfile").Where(z => z.Element("InkZoneProfile").Attribute("Side").Value == "Front").SingleOrDefault();
And this:
XmlDoc.Element("JMF")
.Elements("InkZoneProfile").Where(InkZoneProfile => InkZoneProfile.Attribute("Side")
.Value == "Front").FirstOrDefault().AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
I've built this queries following those examples:
Select XElement where child element has a value
Insert XElements after a specific node
LINQ-to-XML XElement query NULL
But it didn't worked as expected. What is wrong with the LINQ Query ? From what i've read it should work (logically reading the expression i can understand it).
Thanks
EDIT-1: Entire writexml Method
public void writexml(xmldatalist XMLList, variables GlobalVars)
{
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
IndentChars = "\t",
NewLineChars = Environment.NewLine,
NewLineHandling = NewLineHandling.Replace,
Encoding = new UTF8Encoding(false)
};
string DesktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
string FileExtension = ".xml";
string PathString = Path.Combine(DesktopFolder, "XML");
System.IO.Directory.CreateDirectory(PathString);
foreach (List<xmldata> i in XMLList.XMLArrayList)
{
int m = 0;
foreach (var x in i)
{
string XMLFilename = System.IO.Path.GetFileNameWithoutExtension(x.xml_filename);
GlobalVars.FullPath = Path.Combine(PathString, XMLFilename + FileExtension);
if (!File.Exists(GlobalVars.FullPath))
{
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XElement("JMF",
new XAttribute("SenderID", "InkZone-Controller"),
new XAttribute("Version", "1.2"),
new XElement("Command",
new XAttribute("ID", "cmd.00695"),
new XAttribute("Type", "Resource"),
new XElement("ResourceCmdParams",
new XAttribute("ResourceName", "InkZoneProfile"),
new XAttribute("JobID", "K_41"),
new XElement("InkZoneProfile",
new XAttribute("ID", "r0013"),
new XAttribute("Class", "Parameter"),
new XAttribute("Locked", "False"),
new XAttribute("Status", "Available"),
new XAttribute("PartIDKeys", "SignatureName SheetName Side Separation"),
new XAttribute("DescriptiveName", "Schieberwerte von DI"),
new XAttribute("ZoneWidth", "32"),
new XElement("InkZoneProfile",
new XAttribute("SignatureName", "SIG1"),
new XElement("InkZoneProfile",
new XAttribute("Locked", "false"),
new XAttribute("SheetName", "S1"),
new XElement("InkZoneProfile",
new XAttribute("Side", "Front"),
new XElement("ColorPoolClass",
new XAttribute("Class", "Parameter"),
new XAttribute("DescriptiveName", "Colors for the job"),
new XAttribute("Status", "Available")
)))))))));
doc.Save(GlobalVars.FullPath);
XDocument XmlDoc = new XDocument();
XmlDoc = XDocument.Load(GlobalVars.FullPath);
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
}
XmlDoc.Save(GlobalVars.FullPath);
}//Closing !FileExists
}//Closing inner foreach
}//Closing outer foreach
}//Closing writexml method
The problem with your current code is here : Element("JMF").Elements("InkZoneProfile") Since InkZoneProfile is not a direct child of JMF it will not return anything. Use Descendants instead.
Check difference between Elements & Descendants.
This should give you correct result:-
XElement InkZonePath = xdoc.Element("JMF").Descendants("InkZoneProfile")
.SingleOrDefault(z => (string)z.Attribute("Side") == "Front")
After this you can add whatever node you want to add using AddAfterSelf. Also note I have used SingleOrDefault here, but you may get exception if you have multiple matching nodes with this, In that case consider using FirstOrDefault.
Update:
To add new node:-
if (InkZonePath != null)
{
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", "Test"),
new XAttribute("ZoneSettingsX", "Test2")));
}
//Save XDocument
xdoc.Save(#"C:\Foo.xml");
You need to use Descendants method instead Elements:
XElement InkZonePath = XmlDoc.Root.Descendants("InkZoneProfile").Where(z => (string)z.Attribute("Side") == "Front").SingleOrDefault();
if(InkZonePath !=null)
InkZonePath.AddAfterSelf(new XElement("InkZoneProfile",
new XAttribute("Separation", x.colorname),
new XAttribute("ZoneSettingsX", x.colorvalues)));
you can use Descendants instead.
var node = XmlDoc.Descendants("InkZoneProfile").Where(x=> x.Attribute("Side") !=null && x.Attribute("Side").Value == "Front").FirstorDefault();
I am looking to read the following into a key value dictionary for a web service.
Dictionary<string, string> att = new Dictionary<string, string>();
<Req>
<ID>myProg</ID>
<FID>myID</FID>
<att attName="fName"><Value></Value></att>
<att attName="lName"><Value>Doe</Value></att>
<att attName="Add"><Value>222 Fork Drive</Value></att>
</Req>
I know I can do foreach for the the part where there is actually a attName and value, how can I do that for the first two parts?
Someone said what I have done so far:
void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load("myxmldoc.xml");
var re = doc;
foreach (System.Xml.XmlElement r in re)
{
Console.WriteLine(r);
}
}
Thanks
You can use this code
var root = XElement.Parse(xml);
var att = root.Nodes().Where(n => n.NodeType == XmlNodeType.Element)
.Select(node =>
{
var element = (XElement)node;
return element.Name.LocalName.Equals("att")
? Tuple.Create(element.Attribute("attName").Value, ((XElement)element.FirstNode).Value)
: Tuple.Create(element.Name.LocalName, element.Value);
})
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2);
How should I add X there in XElement ?
XDocument triggerDocument = new XDocument(
new XDeclaration("1.0", "utf-8", null));
XElement triggerRoot = new XElement("config",
new XElement("maketool-config",
new XElement("hmi", new XElement("Messages",X))));
triggerDocument.Add(triggerRoot);
triggerDocument.Save(Path.Combine(outPath, "_triggers.xml"));
for (int i = 0; i <= events.Count; i++)
{
foreach (var item in events)
{
triggerRoot.Add(new XElement("n",
new XAttribute("page", item.page),
new XAttribute("sequence", item.sequence),
new XAttribute("priority", item.priority),
new XAttribute("errorText", item.errorText)
));
}
}
so it should look like this :
<?xml version="1.0" encoding="utf-8"?>
<config schema ="sdk-hmi.xsd">
<maketool-config>
<hmi>
<messages>
<n page="" sequence="" priority="" errorText="" />
<n page="" sequence="" priority="" errorText="" />
<n page="" sequence="" priority="" errorText="" />
<n page="" sequence="" priority="" errorText="" />
<n page="" sequence="" priority="" errorText="" />
</messages>
</hmi>
</maketool-config>
</config>
You can pass an XElement[] or IEnumerable<XElement> to XElement's constructor:
var messages = events.Select(item => new XElement("n",
new XAttribute("page", item.page),
new XAttribute("sequence", item.sequence),
new XAttribute("priority", item.priority),
new XAttribute("errorText", item.errorText)
));
XDocument triggerDocument = new XDocument(
new XDeclaration("1.0", "utf-8", null));
XElement triggerRoot = new XElement("config",
new XElement("maketool-config",
new XElement("hmi",
new XElement("Messages", messages))) // <<<--- This is the important part.
);
triggerDocument.Add(triggerRoot);
You can try this:
XDocument triggerDocument = new XDocument(
new XDeclaration("1.0", "utf-8", null));
XElement triggerRoot = new XElement("config",
new XElement("maketool-config",
new XElement("hmi", new XElement("Messages"))));
triggerDocument.Add(triggerRoot);
XElement msgNode = triggerRoot.Elements("Messages")
.SingleOrDefault();
if (msgNode != null)
{
foreach (var item in events)
{
msgNode.Add(new XElement("n",
new XAttribute("page", item.page),
new XAttribute("sequence", item.sequence),
new XAttribute("priority", item.priority),
new XAttribute("errorText", item.errorText)
));
}
}
May this will help to add nodes...
//file name
string filename = #"d:\temp\XMLFile2.xml";
//create new instance of XmlDocument
XmlDocument doc = new XmlDocument();
//load from file
doc.Load(filename);
//create node and add value
XmlNode node = doc.CreateNode(XmlNodeType.Element, "Genre_Genre_Country", null);
node.InnerText = "this is new node";
//add to elements collection
doc.DocumentElement.AppendChild(node);
//save back
doc.Save(filename);
I want the function below to read an XML file and save all the data into the UserClassDict that is passed into the function. The UserClassDict saves the a (username,User Class). The User Class has a property List<int> ControlNumber , where it stores the ControlNumbers.
The XML that it is trying to read looks like this :
<UserClassDictionary>
<adolan>
<ControlNumber>791301</ControlNumber>
</adolan>
<afeazell>
<ControlNumber>790253</ControlNumber>
</afeazell>
<asnyder>
<ControlNumber>790210</ControlNumber>
<ControlNumber>790308</ControlNumber>
</asnyder>
<semery/>
<showard/>
<talexander/>
</UserClassDictionary>
The problem that I'm having is that the LINQ in the function doesn't seem distinguish between the different xml nodes. It doesn't seem to set the node to the Key and the node to the Value.
static void XMLToDictionary(Dictionary<string,User> UserClassDict)
{
XmlDocument doc = new XmlDocument();
doc.Load("UserClassDictionary.xml");
StringWriter sw = new StringWriter();
XmlTextWriter tx = new XmlTextWriter(sw);
doc.WriteTo(tx);
string str = sw.ToString();
XDocument document = XDocument.Parse(str);
foreach (XElement element in document.Descendants().Where(p => p.HasElements == false))
{
int keyInt = 0;
string keyName = element.Name.LocalName;
while (UserClassDict.ContainsKey(keyName))
keyName = element.Name.LocalName + "_" + keyInt++;
UserClassDict.Add(keyName, element.Value);
}
}
First, you don't need a StreamWriter or XmlTextWriter. You can just use XElement.Load() instead.
XElement root = XElement.Load("UserClassDictionary.xml");
Dictionary<string, List<string>> values = new Dictionary<string, List<string>>();
foreach(XElement subNode in root.Elements().Where(x => x.Elements().Count() > 0))
{
values.Add(subNode.Name.LocalName, subNode.Elements("ControlNumber")
.Select(x => x.Value).ToList());
}
Your other reason was you were using Descendents instead of Elements in your loop. This looked for EVERY sub node in your xml, even gradchild nodes.
You can parse your xml and create dictionary of users with Linq to XML:
XDocument xdoc = XDocument.Load("UserClassDictionary.xml");
Dictionary<string, User> users =
xdoc.Root.Elements()
.Select(u => new User {
Name = u.Name.LocalName,
ControlNumber = u.Elements().Select(cn => (int)cn).ToList()
})
.Where(u => ControlNumber.Any())
.ToDictionary(u => u.Name);
Or if you wish to update existing dictionary
var users = from u in xdoc.Root.Elements()
where u.Elements().Any()
select new User {
Name = u.Name.LocalName,
ControlNumber = u.Elements().Select(cn => (int)cn).ToList()
};
foreach(var user in users)
UserClassDict.Add(user.Name, user);
I want to write a Dictionary<int, List<Object>> data to XML file. I tried below code:
Dictionary<int, List<Author>> _empdata = new Dictionary<int, List<Author>>();
List<Author> _author1 = new List<Author> { new Author() { id = 1, name = "Tom", age = 25 } };
List<Author> _author2 = new List<Author> { new Author() { id = 2, name = "Jerry", age = 15 } };
_empdata.Add(1, _author1);
_empdata.Add(2, _author2);
string fileName = "Author_" + string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}", DateTime.Now);
string filePath = Path.Combine(Directory.GetCurrentDirectory(), fileName) + ".xml";
XElement elem = new XElement("StackOverflow");
foreach (KeyValuePair<int,List<Author>> pair in _empdata)
{
elem = new XElement("AUTHORDATA",
from item in pair.Value
select new XElement("AUTHOR",
new XElement("ID", item.id),
new XElement("NAME", item.name),
new XElement("AGE", item.age)
));
}
elem.Save(filePath);
My expected output is:
<AUTHORDATA>
<AUTHOR>
<ID>1</ID>
<NAME>Tom</NAME>
<AGE>25</AGE>
</AUTHOR>
<AUTHOR>
<ID>2</ID>
<NAME>Jerry</NAME>
<AGE>15</AGE>
</AUTHOR>
</AUTHORDATA>
But,I am getting below records in XML file:
<AUTHORDATA>
<AUTHOR>
<ID>2</ID>
<NAME>Jerry</NAME>
<AGE>15</AGE>
</AUTHOR>
</AUTHORDATA>
It is only writing the last List record in XML file every time. How can I fix this ? Note: The format of the XML file will be like above.
Any help is appreciated.
[EDIT] Awww sorry, I was misunderstanding your request:
XElement ad = new XElement("AUTHORDATA");
XElement elem = new XElement("StackOverflow", ad);
foreach (KeyValuePair<int, List<Author>> pair in _empdata)
{
ad.Add(new XElement("AUTHORDATA",
from item in pair.Value
select new XElement("AUTHOR",
new XElement("ID", item.id),
new XElement("NAME", item.name),
new XElement("AGE", item.age)
));
}
elem.Save(filePath);
You need to add node created in loop to AUTHORDATA node:
var authorData = new XElement("AUTHORDATA");
var root = new XElement("StackOverflow", authorData);
foreach (KeyValuePair<int,List<Author>> pair in _empdata)
{
authorData.Add(
from item in pair.Value
select new XElement("AUTHOR",
new XElement("ID", item.id),
new XElement("NAME", item.name),
new XElement("AGE", item.age)
));
}
root.Save(filePath);