2
\$\begingroup\$

I have been working on my own form of validation for XML other then xpath, as a toy project.

First off, I use this class to add Validations to the graph to check for and validate they exist.

public class GraphValidation 
{
    public string Target { get; set; } = "";
    public bool Result = false;
}

Next I use this class to output a consistent number of strings:

public class GraphRow
{

    //clone the columns down to show parent child relationships rather then just a single endpoint on each line.

    public GraphRow(string[] clone)
    {
        int i = 0;
        var values = clone.Where(x => x != "" & x != null);
        foreach (var value in values)
        {
            Columns[i] = value ?? "";
            i = i + 1;
        }
    }
    //Strings.
    public string[] Columns = new string[25];

    //indexing the strings.
    public virtual string this[int i]
    {
        get { if (i >= 0 && i < Columns.Length) { return this.Columns[i].ToString(); } else { return null; } }
        set { if (i >= 0 && i < Columns.Length) { this.Columns[i] = value; } else { return; } }
    }

    #region IDisposable Support

    private static bool disposedValue = false; // To detect redundant calls


    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.

            disposedValue = true;
        }
    }
    // This code added to correctly implement the disposable pattern.
    public void Dispose()
    {
        // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
        Dispose(true);
        // TODO: uncomment the following line if the finalizer is overridden above.
        // GC.SuppressFinalize(this);
    }

    #endregion IDisposable Support
}
public static class Grapher
{
    public static Dictionary<string, object> roots { get; set; }
    //Row store
    public static List<GraphRow> Rows = new List<GraphRow>();

    //Current row initialization
    private static GraphRow CurrentRow = new GraphRow(new string[25]);

    public static List<GraphValidation> Validations = new List<GraphValidation>();

    public static List<GraphValidation> AddValidation(this List<GraphValidation> validations, string Attribute)
    {
        var Obj = new GraphValidation();

        Obj.Target = Attribute;

        Validations.Add(Obj);

        return Validations;
    }
    public static List<GraphValidation> Validate(this List<GraphValidation> validations)
    {
        if (roots != null)
        {
            var AttributeGraph = Graph(roots);
            //AttributeGraph.ViewThis();
            if (Validations.Count > 0)
            {
                foreach(var validation in validations)
                {
                    validation.Result = AttributeGraph.Select(x => x.ToString()).Any(x => x.Contains(validation.Target));
                }
            }
        }
        return validations;
    }
    private static IQueryable<object> Graph(object roots, int Level = 0)
    {
        if (roots is IDictionary<string, object>)
        {
            foreach (var root in roots as IDictionary<string, object>)
            {
                //set key
                CurrentRow[Level] = root.Key;
                //loop around increasing the level as I dig down
                if (root.Value is IDictionary<string, object>)
                {
                    Graph(root.Value as IDictionary<string, object>, Level + 1);
                }
                else if (root.Value is IList<object>)
                {
                    Graph(root.Value as IList<object>, Level);
                }
                else
                {
                    var ol = Level;
                    while(ol <= CurrentRow.Columns.Length -1 && CurrentRow.Columns[ol + 1] != "" && CurrentRow.Columns[ol + 1] != null)
                    {
                        CurrentRow.Columns[ol + 1] = "";
                        ol += 1;
                    }
                    //add the current row
                    Rows.Add(CurrentRow);
                    //Clone the Current row;
                    CurrentRow = new GraphRow(CurrentRow.Columns);
                }
            }
        }
        else if (roots is List<object>)
        {
            foreach (var value in roots as List<object>)
            {
                if (value is IDictionary<string, object>)
                {
                    Graph(value as IDictionary<string, object>, Level + 1);
                }
                else
                {
                    if (value is IList<object>)
                    {
                        Graph(value as IList<object>, Level);
                    }
                }
            }
        }
        else
        {

            //set end of path
            CurrentRow[Level] = roots.ToString();
            var ol = Level;
            while (ol <= CurrentRow.Columns.Length - 1 && CurrentRow.Columns[ol + 1] != "" && CurrentRow.Columns[ol + 1] != null)
            {
                CurrentRow.Columns[ol + 1] = "";
                ol += 1;
            }
            //add the current row
            Rows.Add(CurrentRow);
            //Clone the Current row;
            CurrentRow = new GraphRow(CurrentRow.Columns);
        }

        return (from row in Rows
                select new { Column0 = row.Columns[0], Column1 = row.Columns[1], Column2 = row.Columns[2], Column3 = row.Columns[3], Column4 = row.Columns[4], Column5 = row.Columns[5], Column6 = row.Columns[6], Column7 = row.Columns[7], Column8 = row.Columns[8], Column9 = row.Columns[9], Column10 = row.Columns[10], Column11 = row.Columns[11], Column12 = row.Columns[12], Column13 = row.Columns[13], Column14 = row.Columns[14], Column15 = row.Columns[15], Column16 = row.Columns[16], Column17 = row.Columns[17], Column18 = row.Columns[18], Column19 = row.Columns[19], Column20 = row.Columns[20], Column21 = row.Columns[21], Column22 = row.Columns[22], Column23 = row.Columns[23], Column24 = row.Columns[24] }).AsQueryable();
      }
}

I then usually pass it into the Viewthis() function, which is just something I whipped together to view stuff:

public static class Extensions
{
    const int MaxDepth = 2;
    private static int s_depth;

    public static int Depth { get => s_depth; set => s_depth = value; }
    public static void handler(object o, EventArgs _)
    {
        var otype = o as DataGridView;
        var CurrectRecipeId = (from row in otype.Rows.Cast<DataGridViewRow>()
                               where row.Selected
                               from cell in row.Cells.Cast<DataGridViewCell>()
                               select cell.Value).FirstOrDefault();

        var objObject = (from recipeParts in otype.Rows.Cast<DataGridViewRow>()
                         where recipeParts.Cells[0].Value.ToString() == CurrectRecipeId.ToString()
                         from parts in recipeParts.Cells.Cast<DataGridViewCell>()
                         select new { parts.OwningColumn.Name, parts.Value });

         objObject.ViewThis(handler);
    }
    public static void ViewThis(this IEnumerable<object> viewThis, EventHandler func = null)
    {
        if (Depth != MaxDepth)
        {
            Depth = Depth + 1;
            var ThreadForm = new Task(() =>
            {
                Thread.CurrentThread.TrySetApartmentState(ApartmentState.STA);
                Form form = new Form();
                DataGridView dgv = new DataGridView();
                form.WindowState = FormWindowState.Maximized;
                dgv.DataBindingComplete += (o, _) =>
                 {
                     var dataGridView = o as DataGridView;
                     if (dataGridView != null)
                     {
                         dataGridView.Anchor = AnchorStyles.Top | AnchorStyles.Left;
                         dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
                         dataGridView.Dock = DockStyle.Fill;
                         dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
                     }
                 };
                dgv.DoubleClick += (o, _) => func(o, _);

                dgv.DataSource = viewThis.ToList();

                form.Controls.Add(dgv);

                Application.Run(form);
            });
            try
            {
                ThreadForm.Start();
                ThreadForm.Wait();
                Depth = Depth - 1;
            }
            catch (Exception)
            {

                throw;
            }
        }
    }

This is how I have it working now:

var xDoc = XDocument.Load(File.ReadAllText("2977501.xml").ValidateXML().ToStream());
Dictionary<string, object> roots = new Dictionary<string, object>();
XmlExtensions.XmlToDictionary(xDoc.Elements().First(), roots);
Grapher.roots = roots;
foreach( var validation in Grapher.Validations.AddValidation("externalFileLink").AddValidation("consumerStorageInstructions").Validate())
{
    if(validation.Result == false)
    {
        Console.WriteLine("Validation for " + validation.Target + " failed");
    }
    else
    {
        Console.WriteLine("Validation Passed: " + validation.Target);
    }
}

Can you review this and give me some points on what I can do to improve it?

\$\endgroup\$
  • 2
    \$\begingroup\$ XML has absolutely fantastic schema validation. I’d suggest learning about it. Don’t reinvent a wheel if you don’t have to. docs.microsoft.com/en-us/dotnet/standard/data/xml/… \$\endgroup\$ – RubberDuck Sep 2 '18 at 22:45
  • \$\begingroup\$ You forgot to include your XmlToDictionary method. Also, what exactly is your validation code meant to do? As far as I can tell it's looking for the presence of certain attributes, anywhere in the given XML, but I don't see how useful that is on its own. \$\endgroup\$ – Pieter Witvoet Sep 3 '18 at 9:52
  • \$\begingroup\$ XmlToDictionary is just a slightly modified version of this itdevspace.com/2012/07/parse-xml-to-dynamic-object-in-c.html Well this is meant to improve the quality of data customers using machine to machine communication. IE I verify that you sent me certain attribute, and if not then i send a message back telling you to send me required attribute. \$\endgroup\$ – BanMe Sep 3 '18 at 14:39
  • \$\begingroup\$ @RubberDuck Yes, excellent validation but not so much in the generation of response messages, I need to handle it outside of just Schema validation and setting the MinOcccurs and Constraints, and to be honest XML is ugly and i don't like writing it or having to modify a XSD 300+ times for my customers to add individual validations. \$\endgroup\$ – BanMe Sep 3 '18 at 21:30
  • 1
    \$\begingroup\$ @BanMe: you may want to include your modified version of that method in your post. Also, since this looks like a very specific kind of validation, you should probably explain the expected format, maybe include an example as well. And why does this code work on a dictionary rather than the original xml? \$\endgroup\$ – Pieter Witvoet Sep 5 '18 at 8:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.