I have an XML file that begins at 1,030KB. After I run my code, which is adding an element to a section of the xml file, the file reduces to a size of 580kb. I think this might be a reason why a secondary application reading this file, cannot read the newly generated file. I am not sure why this is happening. Is there a way to stop Linq to XML from compressing the file?
XDocument xDoc = XDocument.Load(cust_file);
XElement parentXElement = xDoc.XPathSelectElement("LastLayout2/CommandBars");
XElement refXElement = xDoc.XPathSelectElement("LastLayout2/CommandBars/CommandBar[#Title = 'Standard']/Controls");
XElement temp = refXElement.XPathSelectElement("Control[#Parameter = 'GLOBAL!QMS_Launcher.Main']");
if (temp == null)
{
XElement newElement = new XElement("Control");
XAttribute classAt = new XAttribute("Class", "CXTPControlButton");
XAttribute idAt = new XAttribute("Id", "0");
XAttribute paramAt = new XAttribute("Parameter", "GLOBAL!QMS_Launcher.Main");
XAttribute custIdAt = new XAttribute("CustomIconId", "68267");
XElement customIcon = new XElement("CustomIcon");
XElement icon = new XElement("Icon");
XAttribute width = new XAttribute("Width", "16");
XAttribute data = new XAttribute("Data", "ABAAAAAAFCAAAAAAEDAAICAAAAAAABAAAAAAABAAAAAABAAAACAADAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPPAAAAPPAAAAPPAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPGAAAAAAJNAAAAAAFCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANAAAAAAAHFAAAAAACHAAAAAAALAAAAAAPPAAAAAAJPAAAAAAHCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPFAAAAAAJOAAAAAAPPAAAAAAPPAAAAAAPPAAAAAALOABJAAACGDNJIAADNGJCGAAGJGAEAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDAAAAAANPAAAAAAMPAAAAAAJLAAAAAAMHAAAAAAFFAAAAAAODAAAAAAAKMGFEAAEIPPGKAAPPPHDFAAPHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMAAAAAAPPAAAAAAPGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANGAAAAAAPPCBMAAAIFIHOEAAIHJFJDAAJFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOAAAAAAEPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPHAAAAAAPPGBOAAAKGILHHAAILILHHAAILJJEGAAJJFEMCAAFEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEPAAAAAAEOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHJAAAAAAPPGCJBAAAHPPGKAAPPPPGKAAPPPPGKAAPPMPEKAAMPPDJCAAPDAAAAAAAAAAAAAAAAAAAAAAOMAAAAAAPPAAAAAAJBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALLAAAAAAPPBCFBAAKFILHHAAILILHHAAILILHHAAILILHHAAILKGFEAAKGAAAAAAAAAAAAAAAAAAAAAANJAAAAAAPPAAAAAANMAAAAAABCAAAAAANBAAAAAABIAAAAAAPPAAAAAAHOAEKCAAMEIHOEAAIHIHOEAAIHIHOEAAIHIHOEAAIHJGDEAAJGGCJBAAGCAAAAAAAAAAAAAAFBAAAAAADOAAAAAAPPAAAAAAPPAAAAAAPPAAAAAAPPAAAAAALPMAIAAAHGNOKJAANOPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPOPFKAAOPKFKDAAKFAAAAAAAAAAAAAAKBAAAAAANJAAAAAABPAAAAAABPAAAAAAAMJBPAAALELMEIAALMPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPPPGKAAPPINMIAAINAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
icon.Add(width, data);
customIcon.Add(icon);
newElement.Add(customIcon);
newElement.Add(classAt, idAt, paramAt, custIdAt);
xDoc.Element("LastLayout2").Element("CommandBars").Elements("CommandBar")
.FirstOrDefault(item => item.Attribute("Title") != null && item.Attribute("Title").Value == "Standard")
.Element("Controls").Add(newElement);
xDoc.Save(cust_file);
It sounds like an encoding issue. You're probably saving down as a different encoding to the original document, and different from what the target application is expecting. Hence the change in file size.
It's possible to change the encoding used for the save as described by dbc above:
Create an XmlWriterSettings, set XmlWriterSettings.Encoding as
appropriate, then create an XmlWriter and pass it to XDocument.Save().
Related
I have a string that is displayed in XML but in it I have some invalid chars like string
s = <root> something here <XMLElement>hello</XMLElement> somethig here too </root>
where XMLElement is a List like XMLElement = {"bold", "italic",...} .
What I need is to replace the < and </ if followed by any of the XMLElements to be replaced by > or < depending on the cases.
The <root> is to keep
I have tried so far some regEx
strAux = Regex.Replace(strAux, "bold=\"[^\"]*\"",
match => match.Value.Replace("<", "<").Replace(">", ">"));
or
List<string> startsWith = new List<string> { "<", "</"};
foreach(var stw in startsWith)
{
int nextLt = 0;
while ((nextLt = strAux.IndexOf(stw, nextLt)) != -1)
{
bool isMatch = strAux.Substring(nextLt + 1).StartsWith(BoldElement); // needs to ckeck all the XMLElements
//is element, leave it
if (isMatch)
{
//its not, replace
strAux = string.Format(#"{0}<{1}", strAux.Substring(0, nextLt), strAux.Substring(nextLt +1, strAux.Length - (nextLt + 1)));
}
nextLt++;
}
}
Also tried
XmlDocument doc = new XmlDocument();
XmlElement element = doc.CreateElement("root");
element.InnerText = strAux;
Console.WriteLine(element.OuterXml);
strAux = element.OuterXml.Replace("<root>", "").Replace("</root>", "");
return strAux; But it will repeat the `<root>` too
But nothing worked like I suposed. Is there any different ideias .Thanks
What you have is well-formed XML, so you can use the XML APIs to help you:
Using LINQ to XML (which is generally the better API):
var element = XElement.Parse(s);
element.Value = string.Concat(element.Nodes());
var result = element.ToString();
Or using the older XmlDocument API:
var doc = new XmlDocument();
doc.LoadXml(s);
var root = doc.DocumentElement;
root.InnerText = root.InnerXml;
var result = root.OuterXml;
The result for both is:
<root> something here <XMLElement>hello</XMLElement> somethig here too </root>
See this fiddle for a demo.
You should be using the XmlWriter class.
Sample from the documentation:
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Fragment;
settings.CloseOutput = false;
// Create the XmlWriter object and write some content.
MemoryStream strm = new MemoryStream();
XmlWriter writer = XmlWriter.Create(strm, settings);
writer.WriteElementString("someNode", "someValue");
writer.Flush();
writer.Close();
https://msdn.microsoft.com/en-us/library/system.xml.xmlwriter(v=vs.110).aspx
It sounds like your input is well-formed XML, but you want to escape some of the tags. The issue here is that there's no way for the code to know which tags are valid and which aren't.
One way to do this is to create a list of valid tags.
List<string> validTags = new List<string>() { "root", "..." };
Then use regex to pick out all instances of <tag> or </tag>and replace them if they're not in the list.
Another way which is faster and easier, but requires more information up front, is to create a list of tags which aren't valid.
List<string> invalidTags = new List<string>() { "XMLElement", "..." };
Simple string manipulation will do, now.
string s = GetYourXMLString();
invalidTags.ForEach(t => s = s.Replace($"</{t}>",$"<{t}>")
.Replace($"<{t}>",$"</{t}>"));
The second way should really only be used if you know which foreign tags are making (or will ever make) an appearance. If not the first approach should be used. One clever possibility is to dynamically create the list of valid tags using reflection or a data contract so that changes to the XML spec will be automatically reflected in your code.
For example, if each element is a property of an object, you might get the list like this:
var validTags = typeof(MyObjectType).GetProperties()
.Select(p => p.PropertyName)
.ToList();
Of course, the property names likely won't be the actual tag names, AND often you'll want to only include certain properties. So you make an attribute class to designate the desired properties (let's call it XMLTagName) and then you can do this:
var validTags = typeof(MyObjectType).GetProperties()
.Select(p => p.GetCustomAttribute<XMLTagName>()?.TagName)
.Where(tagName => tagName != null) //gets rid of properties that aren't tagged
.ToList();
Even with all that, you'll still committing the crime of string manipulation on raw XML. After all, the best real solution here is to figure out how to fix the incoming XML to actually contain the data you want. But if that's not a possibility, the above should do the job.
I am trying to figure out how to modify custom XML parts previusly saved in Excel. All the web resources I have found so far explain how to add custom XML parts in Excel. This I already know. But I want to modify existing parts.
The API seems to have only Add method. If Add method is called again it adds additional XML parts.
I use the following code to save my custom XML
XNamespace NS = "http://schema.blabla.com";
var xDoc = new XDocument(
new XDeclaration("1.0", "utf-8", "no"),
new XComment("Custom XML Parts demo"),
new XElement(NS + "demo",
new XElement(NS + "config",
new XElement(NS + "property",
new XAttribute("value", "myVlaue",
new XAttribute("key", "myKey"))))));
Office.CustomXMLPart customXMLPart = workbook.CustomXMLParts.Add(xDoc.ToString(), System.Type.Missing);
I use the following code to retrieve my custom XML
var retrievedXMLParts = workbook.CustomXMLParts.SelectByNamespace(NS.NamespaceName);
//FirstOrDefault always returns first saved data, LastOrDefault needs to be called to get the latest
//var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().FirstOrDefault();
var customXMLPart = retrievedXMLParts.Cast<CustomXMLPart>().LastOrDefault();
var propertiesXML = customXMLPart != null ? customXMLPart.XML : String.Empty;
What I would like to achieve is to check if a custom XML exists update its content instead off adding it as duplicate
I think I have found a solution but it involves iterating through all the custom XML parts, deleting the one you want to update and then add again:
IEnumerator e = workbook.CustomXMLParts.GetEnumerator();
CustomXMLPart p;
while (e.MoveNext())
{
p = (CustomXMLPart) e.Current;
//p.BuiltIn will be true for internal buildin excel parts
if (p != null && !p.BuiltIn && p.NamespaceURI == NS.NamespaceName)
p.Delete();
}
I'm writing an application which will need to index and store information about files fast. I'm currently using XML to store the information using this code:
XmlTextWriter xtw;
xtw = new XmlTextWriter(FilePath, Encoding.UTF8);
xtw.WriteStartDocument();
xtw.WriteStartElement("ApplicationIndex");
xtw.WriteEndElement();
xtw.Close();
XmlDocument xd = new XmlDocument();
FileStream lfile = new FileStream(FilePath, FileMode.Open);
xd.Load(lfile);
XmlElement cl = xd.CreateElement("Application");
cl.SetAttribute("Name", ApplicationName);
XmlElement na = xd.CreateElement("Path");
XmlText natext = xd.CreateTextNode(ApplicationPath);
na.AppendChild(natext);
cl.AppendChild(na);
XmlElement na1 = xd.CreateElement("UseCount");
XmlText natext1 = xd.CreateTextNode("0");
na1.AppendChild(natext1);
cl.AppendChild(na1);
XmlElement na2 = xd.CreateElement("SearchTerm");
XmlText natext2 = xd.CreateTextNode(ApplicationName.ToLower());
na2.AppendChild(natext2);
cl.AppendChild(na2);
xd.DocumentElement.AppendChild(cl);
lfile.Close();
xd.Save(FilePath);
This works fine for creating the file and storing the data, however I'm having trouble searching through the data quickly as there are several hundred nodes in the document. I've tried using Linq to XML to achieve this using this code:
listBox1.Items.Clear();
var doc = XDocument.Load(filePath);
foreach (var child in doc.Descendants("SearchTerm"))
{
if (child.Value.Contains(textBox1.Text.ToLower()))
{
listBox1.Items.Add(child.Value);
}
}
This is very fast however I can't seem to get any information about the selected node. For example I would like to sort the returned results based upon the UseCount (The higher the count the higher up the list). Is there anyway to do this in XML or any other technique to achieve this?
This is what the XML file looks like:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationIndex>
<Application Name="Google Chrome">
<Path>C:\Program Files\Google\Chrome\Chrome.exe</Path>
<UseCount>0</UseCount>
<SearchTerm>google chrome</SearchTerm>
</Application>
<Application Name="Mozilla Firefox">
<Path>C:\Program Files\Mozilla\Firefox\Firefox.exe</Path>
<UseCount>0</UseCount>
<SearchTerm>mozilla firefox</SearchTerm>
</Application>
</ApplicationIndex>
You can Sort your elements by UseCount in descending order like this:
var doc = XDocument.Load(filePath);
var elements = doc.Descendants("Application")
.OrderByDescending(x => (int)x.Element("UseCount"));
In order to search a record by given SearchTerm you can do the following:
var element = doc.Descendants("Application")
.FirstOrDefault(x => (string)x.Element("SearchTerm") == value);
if(element != null)
{
// record found
}
I basically want to know how to insert a XmlDocument inside another XmlDocument.
The first XmlDocument will have the basic header and footer tags.
The second XmlDocument will be the body/data tag which must be inserted into the first XmlDocument.
string tableData = null;
using(StringWriter sw = new StringWriter())
{
rightsTable.WriteXml(sw);
tableData = sw.ToString();
}
XmlDocument xmlTable = new XmlDocument();
xmlTable.LoadXml(tableData);
StringBuilder build = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(build, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
writer.WriteStartElement("dataheader");
//need to insert the xmlTable here somehow
writer.WriteEndElement();
}
Is there an easier solution to this?
Use importNode feature in your document parser.
You can use this code based on CreateCDataSection method
// Create an XmlCDataSection from your document
var cdata = xmlTable.CreateCDataSection("<test></test>");
XmlElement root = xmlTable.DocumentElement;
// Append the cdata section to your node
root.AppendChild(cdata);
Link : http://msdn.microsoft.com/fr-fr/library/system.xml.xmldocument.createcdatasection.aspx
I am not sure what you are really looking for but this can show how to merge two xml documents (using Linq2xml)
string xml1 =
#"<xml1>
<header>header1</header>
<footer>footer</footer>
</xml1>";
string xml2 =
#"<xml2>
<body>body</body>
<data>footer</data>
</xml2>";
var xdoc1 = XElement.Parse(xml1);
var xdoc2 = XElement.Parse(xml2);
xdoc1.Descendants().First(d => d.Name == "header").AddAfterSelf(xdoc2.Elements());
var newxml = xdoc1.ToString();
OUTPUT
<xml1>
<header>header1</header>
<body>body</body>
<data>footer</data>
<footer>footer</footer>
</xml1>
You will need to write the inner XML files in CDATA sections.
Use writer.WriteCData for such nodes, passing in the inner XML as text.
writer.WriteCData(xmlTable.OuterXml);
Another option (thanks DJQuimby) is to encode the XML to some XML compatible format (say base64) - note that the encoding used must be XML compatible and that some encoding schemes will increase the size of the encoded document (base64 adds ~30%).
I'm creating a new XDocument and inserting a root element "profiles" in it, then saving.
if (!System.IO.File.Exists("profiles.xml"))
{
XDocument doc = new XDocument(
new XElement("profiles")
);
doc.Save("profiles.xml", SaveOptions.None);
}
And then later I wanna take users input and add profiles into the already created xml file:
XElement profile =
new XElement(Player.Name,
new XElement("level", Player.Level),
new XElement("cash", Player.Cash)
);
XDocument doc = XDocument.Load("profiles.xml");
List<XElement> profiles = doc.Root.Elements().ToList();
for (int i = 0; i < profiles.Count; i++)
{
if (profiles[i].Name.ToString() == Player.name)
{
profiles[i] = profile;
return;
}
}
profile.Add(profile);
doc.Save("profiles.xml", SaveOptions.None);
But for some reason, it will never add any new profiles?
EDIT: Also, if I manually create a new profile into the xml file, it won't customize either, so the problem is within Saving the file?
You're never actually doing anything to change any of the elements within the XDocument that doc refers to:
If you find an element with the existing name, you're modifying the list, but that won't modify the document. You probably want to use XElement.ReplaceWith:
profiles[i].ReplaceWith(profile);
Note that in this case you're not even trying to save the XML file again (due to the return statement), so it's not really clear what you're trying to achieve in this case.
If you don't find the element, you're adding the profile element to itself, which certainly isn't going to modify the document. I suspect you want:
doc.Root.Add(profile);
In other words, add the new profile element as a new final child of the root element.
EDIT: Here's a different approach to try instead - I'm assuming any one name should only occur once:
XDocument doc = XDocument.Load("profiles.xml");
var existingElement = doc.Root
.Elements()
.Where(x => x.Name.ToString() == Player.name)
.FirstOrDefault();
if (existingElement != null)
{
existingElement.ReplaceWith(profile);
}
else
{
doc.Root.Add(profile);
}
doc.Save("profiles.xml", SaveOptions.None);
Also, I would strongly advise you not to use the player name as the element name. Use it as an attribute value or text value instead, e.g.
XElement profile =
new XElement("player",
new XAttribute("name", Player.Name),
new Attribute("level", Player.Level),
new XAttribute("cash", Player.Cash)
);
That way you won't have problems if the player name has spaces etc. You'd then need to change your query to:
var existingElement = doc.Root
.Elements()
.Where(x => (string) x.Attribute("name)" == Player.name)
.FirstOrDefault();