I'm trying to automate the replacement of translations from one XML file to another. The original translator set the translation in the wrong files, and I'm trying to restore them back with an automated process. The original files are like this:
<version major="3" minor="6" revision="3" build="1" />
<region id="TranslatedStringKeys">
<node id="root">
<children>
<node id="TranslatedStringKey">
<attribute id="Content" type="28" handle="ls::TranslatedStringRepository::s_HandleUnknown" value="Spanish 1" />
<attribute id="ExtraData" type="23" value="" />
<attribute id="Speaker" type="22" value="" />
<attribute id="Stub" type="19" value="True" />
<attribute id="UUID" type="22" value="AAA" />
</node>
<node id="TranslatedStringKey">
<attribute id="Content" type="28" handle="ls::TranslatedStringRepository::s_HandleUnknown" value="Spanish 2" />
<attribute id="ExtraData" type="23" value="" />
<attribute id="Speaker" type="22" value="" />
<attribute id="Stub" type="19" value="True" />
<attribute id="UUID" type="22" value="BBB" />
</node>
</children>
</node>
</region>
<content contentuid="h5f6c914fg7db0g4763g9731g58a5eb60c6ab" Source="1.lsb" Key="AAA">English1</content>
<content contentuid="h95735cfdgc22cg4d38g9679ge071f18d77aa" Source="1.lsb" Key="BBB">English2</content>
My goal would be to compare the value of the attribute 'value' in the attribute which id="UUID" to the Key of each 'content' node, and if it is the same then substitute the value of each 'content' node with the value of the attribute 'value' in the attribute which id="Content", so that it ends like:
<content contentuid="h5f6c914fg7db0g4763g9731g58a5eb60c6ab" Source="1.lsb" Key="AAA">Spanish 1</content>
<content contentuid="h95735cfdgc22cg4d38g9679ge071f18d77aa" Source="1.lsb" Key="BBB">Spanish 2</content>
I have tried to operate with C# and Xml.Linq but I have lots of errors in the build of my code since my experience with it is very limited.
Thank you for your help and time
In case someone has the same issue, I found a solution to it:
XmlDocument docMain = new XmlDocument();
XmlDocument docCommon = new XmlDocument();
docMain.Load("spanish.xml");
docCommon.Load("COMMON.xml");
foreach (XmlElement elem in docMain.FirstChild)
{
if (elem.GetAttribute("Key") != "")
{
foreach (XmlElement att in docCommon.SelectNodes("descendant::save/region/node/children/node"))
{
XmlNodeList AttList = (XmlNodeList)att.ChildNodes;
XmlElement trans = (XmlElement) AttList.Item(0);
XmlElement UUID = (XmlElement)AttList.Item(4);
if (elem.GetAttribute("Key") == UUID.GetAttribute("value"))
{
elem.InnerText= trans.GetAttribute("value");
}
else { }
}
}
else { }
}
docMain.Save("modified.xml");
Feel free to add suggestions in order to optimise code if you feel like it.
Related
I have the following XML File:
<order id="1234">
<users>
<user id="102030" nick="nickname" done="false" />
<user id="123456" nick="nickname" done="false" />
</users>
<machines>
<machine id="123" sd="123" ref="" done="false" />
<machine id="456" sd="456" ref="" done="false" />
<machine id="789" sd="789" ref="" done="false" />
</machines>
</order>
I want to delete the user with the id 102030, so the xml looks like this:
<users>
<user id="123456" nick="nickname" done="false" />
</users>
<machines>
<machine id="123" sd="123" ref="" done="false" />
<machine id="456" sd="456" ref="" done="false" />
<machine id="789" sd="789" ref="" done="false" />
</machines>
</order>
This is my code which doesn't work:
XmlDocument doc = XmlDocument.Load(path);
XmlNodeList nodes = doc.GetElementsByTagName("users");
foreach(XmlNode node in nodes){
foreach(XmlAttribute attribute in node.Attributes){
if(attribute.Name== "id" && attribute.Value == "102030"){
node.RemoveAll();
}
}
}
doc.Save(path);
I am a newbie in C# so I need every help!
Thanks in advance, geibi
XmlNode.RemoveAll() does not remove a node. Instead it:
Removes all the child nodes and/or attributes of the current node.
Thus, instead you need to remove the node from its parent:
node.ParentNode.RemoveChild(node);
I am trying to add new attribute to my xml files in c#. my xml file format is shown below:
<Root MessageOfRoot="Welcome to Xml">
<Header Size="36">
<Parameter Name="ID" Index="0" Value="23" />
<Parameter Name="Name" Index="4" Value="Uncle Bob" />
<Parameter Name="Number" Index="8" Value="4" />
</Header>
<Body Size="0">
<Parameter Index="0" UnitNumber="0" Name="UnitBarcode" Type="Integer" />
<Parameter Index="4" PromotionId="0" Name="PromotionalUnit" Type="Integer" />
</Body>
</Root>
I want to add new attribute my xml file which should be like:
<Root MessageOfRoot="Welcome to Xml">
<Header Size="36" NewAttr="1">
<Parameter Name="ID" Index="0" Value="23" NewAttr="1"/>
<Parameter Name="Name" Index="4" Value="Uncle Bob" NewAttr="1"/>
<Parameter Name="Number" Index="8" Value="4" NewAttr="1"/>
</Header>
<Body Size="0" NewAttr="1">
<Parameter Index="0" UnitNumber="0" Name="UnitBarcode" Type="Integer" NewAttr="1"/>
<Parameter Index="4" PromotionId="0" Name="PromotionalUnit" Type="Integer" NewAttr="1"/>
</Body>
</Root>
To do that i write the following code but i am having problem with adding newAttr to all nodes. How can i add NewAttr to my new xml file?
XmlDocument doc = new XmlDocument();
doc.Load("Path of xml");
XmlAttribute NewAttr = doc.CreateAttribute("NewAttr ");
countAttr.Value = "1";
XmlWriter writer = XmlWriter.Create("output.xml", settings);
You can use the following command to load the XML file:
XDocument doc = XDocument.Load(#"C:\Users\myUser\myFile.xml");
Then you can invoke a function that recursively accesses all nodes of the XML starting from the children nodes of the Root element:
AddNewAttribute(doc.Root.Elements());
The function can be like so:
public static void AddNewAttribute(IEnumerable<XElement> elements)
{
foreach (XElement elm in elements)
{
elm.Add(new XAttribute("newAttr", 1));
AddNewAttribute(elm.Elements());
}
}
Finally, you can save the XML back to the original file using:
doc.Save(#"C:\Users\myUser\myFile.xml");
I have a XML that has a structure similar to this one:
<?xml version="1.0" encoding="UTF-8"?>
<CompanyName>
<AttrContainer>
<Attr type="String">
<Name value="'Name'" />
<Value value="'AttrContainer'" />
</Attr>
<SubContainer>
<AttrContainer value="'WSSMetadata'" />
<AttrContainer>
<Attr type="String">
<Name value="'Name'" />
<Value value="'AttrContainer'" />
</Attr>
<SubContainer>
<WSSMetadata value="'afe2e194-0ce7-4bfc-b446-9623e4fe7189'" />
<AttrContainer>
<Attr type="String">
<Name value="'Name'" />
<Value value="'WSSMetadata'" />
</Attr>
<Attr type="Uuid">
<Name value="'scanID'" />
<Value value="afe2e194-0ce7-4bfc-b446-9623e4fe7189" />
</Attr>
<Attr type="String">
<Name value="'imagePath'" />
</Attr>
<Attr type="String">
<Name value="'imagePathHD'" />
</Attr>
<Attr type="String">
<Name value="'imagePathThumbnail'" />
</Attr>
<Attr type="String">
<Name value="'imagePathGrey'" />
<Value value="'Images/afe2e194-0ce7-4bfc-b446-9623e4fe7189_grey.jpg'" />
</Attr>
<Attr type="String">
<Name value="'imagePathGreyHD'" />
<Value value="'Images/afe2e194-0ce7-4bfc-b446-9623e4fe7189_grey_hd.jpg'" />
</Attr>
<Attr type="String">
<Name value="'imagePathGreyThumbnail'" />
<Value value="'Images/afe2e194-0ce7-4bfc-b446-9623e4fe7189_grey_thumbnail.jpg'" />
</Attr>
</AttrContainer>
</SubContainer>
</AttrContainer>
</SubContainer>
</AttrContainer>
</CompanyName>
and I am trying to parse it using this code (Linq to XML)
var xmlContent = File.ReadAllText(filePathName);
var doc = XDocument.Parse(xmlContent);
var attr = doc.Root.Elements("CompanyName");
var x = attr.ToList();
but it x has no element.
My questions:
What is wrong with this code that I am not able to get the CompanyName element?
How can I get list of all <SubContainer> elements?
When I got the list of <SubContainer> elements, how can I read read and change its content?
I think you want this instead:
var attr = doc.Root.Elements("AttrContainer");
.Elements returns child elements of that name. CompanyName is you root node, and you're trying to search for its children which are AttrContainer.
What is wrong with this code that I am not able to get the companyname element?
The root element of your xml is CompanyName. So what your code is doing, it's essentially asking 'give me all CompanyName elements that are children of my root CompanyName element'. Hence the list is empty.
how can I get list of all SubContainer elements.
You can use
var subContainers = doc.Root.Descendants("SubContainer");
when I got the list of SubContainer elements, how can I read read and change its content?
foreach (var subContainer in subContainers)
{
foreach (var attrContainer in subContainer.Elements("AttrContainer"))
{
var attr = attrContainer.Elements("Attr").FirstOrDefault();
if (attr != null)
{
var oldValue = attr.Attribute("type").Value;
attr.Attribute("type").Value = "something completely different";
}
}
}
This reads and changes the type on each first Attr element (assuming one exists) in all AttrContainers in all SubContainers - hopefully you can derive something meaningful out of that.
doc.Root returns the element <CompanyName>, so further selecting elements named CompanyName won't return any elements. You're effectively trying to select all <CompanyName> elements that are children of <CompanyName>.
This code will select all <SubContainer> elements no matter their depth. I'm suggesting this because your example XML has several <SubContainer> elements.
// Read all Attr elements
IEnumerable<XElement> subContainerElements = doc.Root.Descendants("SubContainer");
foreach (XElement subContainerElement in subContainerElements)
{
// Work with <SubContainer> element here
}
Assuming having the following xml
<test>
<step>
<var name="name1" />
<var name="name2" />
</step>
<step>
<var name="name3" />
<var name="name4" />
</step>
<step>
<var name="name5" />
<var name="name6" />
</step>
</test>
I m using XmlNodeList, seperated by "step". Is there a way to swap or replace a step directly in the xmlnodelist?
Need to make it like this:
<test>
<step>
<var name="name3" />
<var name="name4" />
</step>
<step>
<var name="name1" />
<var name="name2" />
</step>
<step>
<var name="name5" />
<var name="name6" />
</step>
</test>
You can use XDocument class instead of XMLDocument. This will swap the var nodes name3 with name6.
using System.Linq;
using System.Xml.Linq;
class Test
{
static void Main()
{
XDocument document = XDocument.Load("test.xml");
Swap("name3", "name6", document);
document.Save("test.xml");
}
static void Swap(string nameOne, string nameTwo, XDocument document)
{
var nameOneNode = document.Descendants("var").FirstOrDefault(p => p.Attribute("name").Value == nameOne);
var nameTwoNode = document.Descendants("var").FirstOrDefault(p => p.Attribute("name").Value == nameTwo);
nameOneNode.Attribute("name").Value = nameTwo;
nameTwoNode.Attribute("name").Value = nameOne;
}
}
The order of the nodes in an XML file does not necessarily have to be kept when the XML file is read. For example, if your file looks like this:
<xmlcontent>
<node value="Hello" />
<node value="World" />
</xmlcontent>
The XML read may return the nodes like this:
<xmlcontent>
<node value="World" />
<node value="Hello" />
</xmlcontent>
To apply something like an "order" to XML nodes, you need to add an attribute you can sort by, like
<xmlcontent>
<node index="1" value="Hello" />
<node index="2" value="World" />
</xmlcontent>
In that case, "swapping" two elements would come down to swapping the index values.
Finally managed to do it, here is the code:
XmlDocument xml;
XmlNodeList xmlList;
xml = new XmlDocument();
xml.Load(path);
xmlList = xml.GetElementsByTagName("step");
XmlNode refNode = xmlList[1];
XmlNode newNode = xmlList[0];
xml.DocumentElement.InsertAfter(newNode, refNode);
I have got a huge xml document.
something like that
<?xml version="1.0" encoding="utf-8"?>
<elements>
<element id="1" name="france" />
<element id="2" name="usa" />
<element id="3" name="Spaïn" />
<element id="4" name="spain and africa" />
<element id="5" name="italie and Spâin" />
</elements>
I want to have something like this :
string str = "spain";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXML);
// Xpath with regex or something very veloce
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("//element"+ something);
And the xmlNodeList will contains :
<element id="3" name="Spaïn" />
<element id="4" name="france with spâin and africa" />
<element id="5" name="italie and Spain" />
it must ignore the case
And accent
for the moment I've
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("/*/*[contains(concat(' ',translate(translate(#n,translate(#n, 'aaabcdefghiiijklmnopqrstuvwxyzâÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ', ''),''), 'âÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ','aaabcdefghiiijklmnopqrstuvwxyz'),' '),' "+prefix+" ')]");
where #n = #name and prefix is maybe : "spain" or "Spain" or "Spaïn" and it give me 0 solution
Use
//element[contains(concat(' ',translate(#name,'SPAIN','spain'),' '),' spain ')]
Edit: Now, the question has changed, but the answer remains...
Just add these changes in the translation pattern like:
//element[contains(concat(' ',
translate(#name,
'SPAÂâIÏïN',
'spaaaiiin'),
' '),
' spain ')]
Note: Of course, a more general expression would need a more general translation pattern.
string str = "spain";
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXML);
// Xpath with regex or something very veloce
XmlNodeList xmlNodeList = xmlDoc.SelectNodes("//element[contains(#name,'spain')]");
UPDATE:
As the original problem was changed adding the requirement to recognize the word "Spain" not only in all possible capitalizations but also including accented characters, I have updated the solution below so that now "Spain" with â and/or ïÏ is correctly recognized.
Here is a more generic solution than that of #Alejandro:
If we want to select all elements, whose name attribute contains the word "Spain" in any capitalization and if the possible word delimiters are all non-alphabetic characters, then
This XPath expression:
/*/*[contains(
concat(' ',
translate(translate(#name,
translate(#name, $vAlpha, ''),
' '),
$vUpper,
$vLower),
' '
),
' spain '
)
]
when applied on this XML document:
<elements>
<element id="1" name="france" />
<element id="2" name="usa" />
<element id="3" name="Spaïn" />
<element id="4" name="france with spâin and africa" />
<element id="5" name="-Spain!" />
<element id="6" name="spain and africa" />
<element id="7" name="italie and Spain." />
</elements>
selects the following elements:
<element id="3" name="Spaïn"/>
<element id="4" name="france with spâin and africa"/>
<element id="5" name="-Spain!"/>
<element id="6" name="spain and africa"/>
<element id="7" name="italie and Spain."/>
In the above XPath expression $vLower, $vUpper must be substituted with (respectively):
'aaabcdefghiiijklmnopqrstuvwxyz'
and
'âÂABCDEFGHïÏIJKLMNOPQRSTUVWXYZ'
$vAlpha must be substituted by the concatenation of $vLower and $vUpper .