diff options
| author | Roy Ben Shabat <roy.mail.net@gmail.com> | 2025-09-04 12:31:46 +0300 |
|---|---|---|
| committer | Roy Ben Shabat <roy.mail.net@gmail.com> | 2025-09-04 12:31:46 +0300 |
| commit | 13f9257daed202db98442f4a97167fd4d0e09e14 (patch) | |
| tree | 1b735c7212ed42ff808a8ce16b71e6991a44b32c /Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils | |
| parent | 5c8f370f9733b881aea4232391b86a640c218f42 (diff) | |
| download | Tango-13f9257daed202db98442f4a97167fd4d0e09e14.tar.gz Tango-13f9257daed202db98442f4a97167fd4d0e09e14.zip | |
Multiple Improvements.
Diffstat (limited to 'Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils')
| -rw-r--r-- | Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils/DataHelper.cs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils/DataHelper.cs b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils/DataHelper.cs new file mode 100644 index 000000000..6f9b4b22c --- /dev/null +++ b/Software/Visual_Studio_22/Tango.Portal.Chat.Web/Utils/DataHelper.cs @@ -0,0 +1,128 @@ +using Newtonsoft.Json.Linq; +using System.Collections; +using System.Data; +using System.Net; +using System.Text; +using System.Text.Json.Nodes; + +namespace Tango.Portal.Chat.Web.Utils +{ + public static class DataHelper + { + /// <summary> + /// Converts a DataTable into a robust Markdown table string. + /// - HTML-encodes (&, <, >) to avoid HTML injection. + /// - Replaces '|' with | so it doesn't break cell boundaries. + /// - Normalizes CR/LF and turns newlines into <br/> so multi-line cells stay in-row. + /// - Replaces tabs with spaces. + /// - Optionally truncates very long cells. + /// </summary> + public static string ToMarkdownTable(DataTable table, int maxCellChars = 100) + { + if (table == null || table.Columns.Count == 0) return string.Empty; + + string Esc(string? s) + { + if (string.IsNullOrEmpty(s)) return string.Empty; + + // HTML-encode first (safer in Markdown renderers that allow HTML) + string t = WebUtility.HtmlEncode(s); + + // Normalize newlines and tabs + t = t.Replace("\r\n", "\n").Replace("\r", "\n"); + t = t.Replace("\t", " "); + t = t.Replace("\n", "<br/>"); + + // Escape Markdown table pipes via HTML entity (more reliable than backslash) + t = t.Replace("|", "|"); + + if (maxCellChars > 0 && t.Length > maxCellChars) + t = t.Substring(0, maxCellChars) + "…"; + + return t; + } + + var sb = new StringBuilder(); + + // Header row + for (int c = 0; c < table.Columns.Count; c++) + sb.Append("| ").Append(Esc(table.Columns[c].ColumnName)).Append(' '); + sb.AppendLine("|"); + + // Separator row + for (int c = 0; c < table.Columns.Count; c++) + sb.Append("|---"); + sb.AppendLine("|"); + + // Data rows + foreach (DataRow row in table.Rows) + { + for (int c = 0; c < table.Columns.Count; c++) + sb.Append("| ").Append(Esc(row[c]?.ToString())).Append(' '); + sb.AppendLine("|"); + } + + return sb.ToString(); + } + + public static object ToPreview(DataTable dt, int maxRows) + { + var cols = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName).ToArray(); + var rows = new List<Dictionary<string, object?>>(); + int count = 0; + + foreach (DataRow r in dt.Rows) + { + if (count++ >= maxRows) break; + + var d = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase); + foreach (var c in cols) + { + var cell = r[c]; + d[c] = NormalizeCell(cell); + } + rows.Add(d); + } + + // (Optional but helpful) include the full row count + return new { columns = cols, rows, totalRows = dt.Rows.Count }; + } + + private static object? NormalizeCell(object? v) + { + if (v is null || v is DBNull) return null; + + // Newtonsoft JToken -> System.Text.Json node + if (v is JToken jt) + { + // Preserves arrays/objects rather than stringifying + return JsonNode.Parse(jt.ToString(Newtonsoft.Json.Formatting.None)); + } + + // Parse JSON strings (dynamic often arrives as text) + if (v is string s) + { + s = s.Trim(); + if ((s.StartsWith("[") && s.EndsWith("]")) || (s.StartsWith("{") && s.EndsWith("}"))) + { + try { return JsonNode.Parse(s); } catch { /* fall through */ } + } + return s; // plain string + } + + // Flatten enumerables (but not strings) + if (v is IEnumerable en && v is not string) + { + var list = new List<object?>(); + foreach (var item in en) list.Add(NormalizeCell(item)); + return list; + } + + // Optional: normalize DateTime -> ISO 8601 for consistency + if (v is DateTime dt) return dt.ToUniversalTime().ToString("o"); + + // Numbers, bools, etc. pass through + return v; + } + } +} |
