Плагин расфасовки по колонкам

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using DynamicPluginData;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Collections.Specialized;
using System.IO;
using LowLevel;
using System.Net;
using System.Threading;
using System.Collections;
using HtmlAgilityPack;

namespace DatacolDynamicPluginNS
{
    public class DynamicPluginClass
    {

        public static DataTable preExportData(DataTable dataTable, ItemInfo itemInfo, GlobalInfo globalInfo)
        {
           
            //Название поля данных, в которое собрана таблица характеристик 
            string FieldName = "характеристики";

            //Список Xpath выражений для сохранения области, в которой находится название и значение одной характеристики
            List ParameterAreaXpathList = new List {"//tr","//dl[@class='product-spec']" };

            //Список Xpath выражений для сохранения названия характеристики из вырезанной области
            List ParameterNameXpathList = new List {"//th/span", "//span[@class='product-spec__name-inner']"};

            //Список Xpath выражений для сохранения значения характеристики из вырезанной области
            List ParameterValueXpathList = new List { "//td", "//span[@class='product-spec__value-inner']"};

            //Список регулярных выражений для сохранения области, в которой находится название и значение одной характеристики
            List ParameterAreaRegexList = new List ();//{"]*?product-spec[^<>]*?>.*?"} 

            //Список регулярных выражений для сохранения названия характеристики из вырезанной области
            List ParameterNameRegexList = new List ();//{"]*?product-spec__name-inner[^<>]*?>(.*?)<.*?>"}

            //Список регулярных выражений для сохранения значения характеристики из вырезанной области
            List ParameterValueRegexList = new List ();//{"]*?product-spec__value-inner[^<>]*?>(.*?)<.*?>"}

            bool AreasFound = false;
            bool TitlesFound = false;
            DataTable tempDT = new DataTable();

            for (int i = 0; i < dataTable.Rows.Count; i++)
            {
                List Areas = getMatches(dataTable.Rows[i][FieldName].ToString(), ParameterAreaXpathList, ParameterAreaRegexList);

                if (Areas.Count == 0) continue;

                AreasFound = true;

                foreach (string Area in Areas)
                {
                    string Title = getMatch(Area, ParameterNameXpathList, ParameterNameRegexList, 1, true);

                    if (Title == "") continue;

                    TitlesFound = true;

                    if (!dataTable.Columns.Contains(Title))
                    {
                        dataTable.Columns.Add(Title);
                    }
                }
            }


            if (!AreasFound) throw new Exception("Области с характеристиками не найдены");
            if (!TitlesFound) throw new Exception("Названия характеристик не найдены");

            DataTable tempDT2 = new DataTable();

            bool ValuesFound = false;

            for (int i = 0; i < dataTable.Rows.Count; i++)
            {
                #region Get characteristics

                List Areas = getMatches(dataTable.Rows[i][FieldName].ToString(), ParameterAreaXpathList, ParameterAreaRegexList);

                if (Areas.Count == 0) continue;

                foreach (string Area in Areas)
                {
                    string Title = getMatch(Area, ParameterNameXpathList, ParameterNameRegexList, 1, true);

                    if (Title == "") continue;

                    string Value = getMatch(Area, ParameterValueXpathList, ParameterValueRegexList, 1, true);

                    if (String.IsNullOrEmpty(Value)) continue;

                    ValuesFound = true;

                    dataTable.Rows[i][Title] = Value;
                }

                #endregion

            }

            if (!ValuesFound) throw new Exception("Значения характеристик не найдены");
            return dataTable;
        }


        /// 
        /// Получаем список участков
        /// 
        /// 
        /// 
        /// 
        public static List getMatches(string RawParameterSource, List ParameterAreaXpathList, List ParameterAreaRegexList, int RegexGroup = 0, bool StripTags = false)
        {
            List RetVal = new List();

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(RawParameterSource);
            HtmlDocument docin = new HtmlDocument();

            //Поиск по xpath
            foreach (string xpath in ParameterAreaXpathList)
            {
                if (doc.DocumentNode.SelectNodes(xpath) == null) continue;

                foreach (HtmlNode node in doc.DocumentNode.SelectNodes(xpath))
                {
                    RetVal.Add(node.OuterHtml);
                }

            }

            //Поиск по regex
            foreach (string regex in ParameterAreaRegexList)
            {
                MatchCollection matches = Regex.Matches(RawParameterSource, regex, RegexOptions.Singleline | RegexOptions.IgnoreCase);

                foreach (Match match in matches)
                {
                    RetVal.Add(match.Groups[RegexGroup].ToString());
                }
            }

            if (StripTags)
            {
                for (int i = 0; i < RetVal.Count; i++)
                {
                    RetVal[i] = extra.strip_tags(RetVal[i]).Trim();
                }
            }
            return RetVal;
        }

        public static string getMatch(string RawParameterSource, List ParameterAreaXpathList, List ParameterAreaRegexList, int RegexGroup = 0, bool StripTags = false)
        {
            List RetValList = getMatches(RawParameterSource, ParameterAreaXpathList, ParameterAreaRegexList,
                RegexGroup,
                StripTags);

            if (RetValList.Count == 0) return "";

            return RetValList[0];
        }
    }
}