2
\$\begingroup\$

I work with some xml files which have UTF-8 characters like &#x00A9;, &#x00EB; but they are converted to their image counterparts i.e. ©, ë, whenever I use XDocument to modify the file and also the self-closing nodes e.x. <a type="box"/> become <a type="box" /> i.e. an extra whitespace is added to the node, but I want to keep the entities like ©, ë etc as it is and also remove the extra whitespace in the self-closing nodes.

Currently I'm doing it in below fashion (sample example code)

var path = @"C:\Users\Temp\sample.xml";
var content=File.ReadAllText(path);
//replace all "&" with "&amp;" to prevent conversion of entity
var newcontent=content.Replace("&", "&amp;");
File.WriteAllText(path,newcontent);
XDocument doc = XDocument.Load(path, LoadOptions.PreserveWhitespace);
string _count = doc.Descendants("fig").Count().ToString();
doc.Descendants("fig-count").First().Value = _count;
doc.Save(path, SaveOptions.DisableFormatting);
//replace all "&amp" with "&" to go back to the original state and also replace all whitespace
//in the self-closing nodes after the XDocument process complets
var x=File.ReadAllText(path);
var y=x.Replace(" />", "/>");
File.WriteAllText(path,y.Replace("&amp;", "&"));

Can anyone show me a more efficient way of doing this?

\$\endgroup\$
2
  • \$\begingroup\$ You shouldn't need to do this. Anyone receiving and processing XML shouldn't care about the difference between &#x00EB; and ë, and therefore anyone producing XML shouldn't need to care about the difference either. You are either doing unnecessary work, or you are doing a workaround for a bug in a receiving program where it would be better to fix the bug. \$\endgroup\$ Commented Jan 10, 2018 at 17:21
  • \$\begingroup\$ @MichaelKay I know its valid to keep it that way but our vendor does not want that, that is why I'm trying to do this in a easy and efficient way... \$\endgroup\$
    – Don_B
    Commented Jan 11, 2018 at 1:27

1 Answer 1

2
\$\begingroup\$

You can do something like this (using your previously posted question code as demo)

Regex regex = new Regex(@"(?<=\\tag\s?\{)[^\}]+(?=\})");
string xml = escape_string(File.ReadAllText(@"C:\Users\Temp\Desktop\test.xml"), 0);
var xdoc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace);
xdoc.DocumentType.InternalSubset = null;
foreach (var formulaNode in xdoc.Descendants("disp-formula"))
{
    var matches = regex.Matches(formulaNode.Value);
    if (matches.Count == 0)
    continue;

    var id = "deqn" + matches[0].Value;
    if (matches.Count > 1)
    id += "-" + matches[matches.Count - 1].Value;

    formulaNode.Attribute("id").Value = id;
}
xdoc.Save(@"C:\Users\Temp\Desktop\test.xml", SaveOptions.DisableFormatting);
File.WriteAllText(@"C:\Users\Temp\Desktop\test.xml", escape_string(xdoc.ToString(), 1));
}

private static string escape_string(string input_string, int option)
{
    switch (option)
    {
        case 0:
        return input_string.Replace("&", "&amp;");
        case 1:
        var x=input_string.Replace(" />", "/>");
        var y=x.Replace("&amp;", "&");
        var z=y.Replace("\r\n<!DOCTYPE","<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<!DOCTYPE");
        return z;

        default:
        return null;

    }
}

The code xdoc.DocumentType.InternalSubset = null; is to stop the Parse() method to create a extra [] in the dtd declaration and also the xml declaration was getting deleted so I added that to a replace in the method escape_string.

Not sure whether it is faster than your posted method though :)

Code for operating on multiple files in a given path (assuming the xml files inside sub-folder 'xml' are only to be processed)

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
using System.Windows.Forms;
using System.Xml.Linq;

namespace test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string path = textBox1.Text;
            var files = Directory.GetDirectories(path, "xml", SearchOption.AllDirectories)
                .SelectMany(d => Directory.GetFiles(d, "*.xml")).ToArray();
            foreach (var file in files)
            {
                try
                {
                    Regex regex = new Regex(@"(?<=\\tag\s?\{)[^\}]+(?=\})");
                    string xml = escape_string(File.ReadAllText(file), 0);
                    var xdoc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace);
                    xdoc.DocumentType.InternalSubset = null;
                    foreach (var formulaNode in xdoc.Descendants("disp-formula"))
                    {
                        var matches = regex.Matches(formulaNode.Value);
                        if (matches.Count == 0)
                            continue;

                        var id = "deqn" + matches[0].Value;
                        if (matches.Count > 1)
                            id += "-" + matches[matches.Count - 1].Value;

                        if (formulaNode.Attribute("id") != null)
                        {
                            formulaNode.Attribute("id").Value = id;
                        }
                        formulaNode.SetAttributeValue("id", id);

                    }
                    xdoc.Save(file, SaveOptions.DisableFormatting);
                    File.WriteAllText(file, escape_string(xdoc.ToString(), 1));
                }
                catch (Exception ex)
                {
                    string filePath = path + @"\Error.log";
                    using (StreamWriter writer = new StreamWriter(filePath, true))
                    {
                        writer.WriteLine(file + ":\r\n---------------------------------------------------\r\n" + ex.Message);
                    }
                }
            }

            MessageBox.Show("Process Complete!");

        }

        private static string escape_string(string input_string, int option)
        {
            switch (option)
            {
                case 0:
                    return input_string.Replace("&", "&amp;");
                case 1:
                    var x = input_string.Replace(" />", "/>");
                    var y = x.Replace("&amp;", "&");
                    var z = y.Replace("\r\n<!DOCTYPE", "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<!DOCTYPE");
                    return z;

                default:
                    return null;

            }
        }
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ it sure looks nicer than mine, but not sure it is faster or not..:) \$\endgroup\$
    – Don_B
    Commented Jan 12, 2018 at 7:23
  • \$\begingroup\$ btw, how can I do this process in multiple xml files in a given path? \$\endgroup\$
    – Don_B
    Commented Jan 12, 2018 at 8:03
  • \$\begingroup\$ @Don_B check the updated answer...hope this helps \$\endgroup\$ Commented Jan 12, 2018 at 8:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.