using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using System.Xml.XPath; namespace AxCopilot.Services.Agent; public class XmlTool : IAgentTool { public string Name => "xml_tool"; public string Description => "Parse and query XML documents. Actions: 'parse' — parse XML file/string and return structure summary; 'xpath' — evaluate XPath expression and return matching nodes; 'to_json' — convert XML to JSON; 'format' — pretty-print XML with indentation."; public ToolParameterSchema Parameters { get { ToolParameterSchema toolParameterSchema = new ToolParameterSchema(); Dictionary dictionary = new Dictionary(); ToolProperty obj = new ToolProperty { Type = "string", Description = "Action: parse, xpath, to_json, format" }; int num = 4; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "parse"; span[1] = "xpath"; span[2] = "to_json"; span[3] = "format"; obj.Enum = list; dictionary["action"] = obj; dictionary["path"] = new ToolProperty { Type = "string", Description = "XML file path (optional if 'xml' is provided)" }; dictionary["xml"] = new ToolProperty { Type = "string", Description = "XML string (optional if 'path' is provided)" }; dictionary["expression"] = new ToolProperty { Type = "string", Description = "XPath expression (for 'xpath' action)" }; toolParameterSchema.Properties = dictionary; num = 1; List list2 = new List(num); CollectionsMarshal.SetCount(list2, num); CollectionsMarshal.AsSpan(list2)[0] = "action"; toolParameterSchema.Required = list2; return toolParameterSchema; } } public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default(CancellationToken)) { string text = args.GetProperty("action").GetString() ?? ""; JsonElement value; string text2 = (args.TryGetProperty("xml", out value) ? (value.GetString() ?? "") : ""); JsonElement value2; string text3 = (args.TryGetProperty("path", out value2) ? (value2.GetString() ?? "") : ""); JsonElement value3; string xpath = (args.TryGetProperty("expression", out value3) ? (value3.GetString() ?? "") : ""); try { if (string.IsNullOrEmpty(text2) && !string.IsNullOrEmpty(text3)) { string text4 = (Path.IsPathRooted(text3) ? text3 : Path.Combine(context.WorkFolder, text3)); if (!context.IsPathAllowed(text4)) { return Task.FromResult(ToolResult.Fail("경로 접근 차단: " + text4)); } if (!File.Exists(text4)) { return Task.FromResult(ToolResult.Fail("파일 없음: " + text4)); } text2 = File.ReadAllText(text4); } if (string.IsNullOrEmpty(text2)) { return Task.FromResult(ToolResult.Fail("'xml' 또는 'path' 중 하나를 지정해야 합니다.")); } XDocument doc = XDocument.Parse(text2); if (1 == 0) { } Task result = text switch { "parse" => Task.FromResult(ParseSummary(doc)), "xpath" => Task.FromResult(EvalXPath(doc, xpath)), "to_json" => Task.FromResult(XmlToJson(doc)), "format" => Task.FromResult(FormatXml(doc)), _ => Task.FromResult(ToolResult.Fail("Unknown action: " + text)), }; if (1 == 0) { } return result; } catch (XmlException ex) { return Task.FromResult(ToolResult.Fail("XML 파싱 오류: " + ex.Message)); } catch (Exception ex2) { return Task.FromResult(ToolResult.Fail("XML 처리 오류: " + ex2.Message)); } } private static ToolResult ParseSummary(XDocument doc) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(6, 1, stringBuilder2); handler.AppendLiteral("Root: "); handler.AppendFormatted(doc.Root?.Name.LocalName ?? "(none)"); stringBuilder3.AppendLine(ref handler); if (doc.Root != null) { XNamespace xNamespace = doc.Root.Name.Namespace; if (!string.IsNullOrEmpty(xNamespace.NamespaceName)) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(11, 1, stringBuilder2); handler.AppendLiteral("Namespace: "); handler.AppendFormatted(xNamespace.NamespaceName); stringBuilder4.AppendLine(ref handler); } int value = doc.Descendants().Count(); int value2 = doc.Descendants().SelectMany((XElement e) => e.Attributes()).Count(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(10, 1, stringBuilder2); handler.AppendLiteral("Elements: "); handler.AppendFormatted(value); stringBuilder5.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(12, 1, stringBuilder2); handler.AppendLiteral("Attributes: "); handler.AppendFormatted(value2); stringBuilder6.AppendLine(ref handler); List list = doc.Root.Elements().Take(20).ToList(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(22, 1, stringBuilder2); handler.AppendLiteral("Top-level children ("); handler.AppendFormatted(doc.Root.Elements().Count()); handler.AppendLiteral("):"); stringBuilder7.AppendLine(ref handler); foreach (XElement item in list) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(16, 2, stringBuilder2); handler.AppendLiteral(" <"); handler.AppendFormatted(item.Name.LocalName); handler.AppendLiteral("> ("); handler.AppendFormatted(item.Elements().Count()); handler.AppendLiteral(" children)"); stringBuilder8.AppendLine(ref handler); } } return ToolResult.Ok(stringBuilder.ToString()); } private static ToolResult EvalXPath(XDocument doc, string xpath) { if (string.IsNullOrEmpty(xpath)) { return ToolResult.Fail("XPath 'expression'이 필요합니다."); } List list = doc.XPathSelectElements(xpath).Take(50).ToList(); if (list.Count == 0) { return ToolResult.Ok("매칭 노드 없음."); } StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2); handler.AppendLiteral("매칭: "); handler.AppendFormatted(list.Count); handler.AppendLiteral("개 노드"); stringBuilder2.AppendLine(ref handler); foreach (XElement item in list) { string text = item.ToString(); if (text.Length > 500) { text = text.Substring(0, 500) + "..."; } stringBuilder.AppendLine(text); } return ToolResult.Ok(stringBuilder.ToString()); } private static ToolResult XmlToJson(XDocument doc) { string text = JsonSerializer.Serialize(XmlToDict(doc.Root), new JsonSerializerOptions { WriteIndented = true }); if (text.Length > 50000) { text = text.Substring(0, 50000) + "\n... (truncated)"; } return ToolResult.Ok(text); } private static Dictionary XmlToDict(XElement el) { Dictionary dictionary = new Dictionary(); foreach (XAttribute item in el.Attributes()) { dictionary["@" + item.Name.LocalName] = item.Value; } List> list = (from e in el.Elements() group e by e.Name.LocalName).ToList(); foreach (IGrouping item2 in list) { List list2 = item2.ToList(); if (list2.Count == 1) { XElement xElement = list2[0]; dictionary[item2.Key] = (xElement.HasElements ? ((IEnumerable)XmlToDict(xElement)) : ((IEnumerable)xElement.Value)); } else { dictionary[item2.Key] = ((IEnumerable)list2).Select((Func)((XElement c) => c.HasElements ? ((IEnumerable)XmlToDict(c)) : ((IEnumerable)c.Value))).ToList(); } } if (!el.HasElements && list.Count == 0 && !string.IsNullOrEmpty(el.Value)) { dictionary["#text"] = el.Value; } return dictionary; } private static ToolResult FormatXml(XDocument doc) { StringBuilder stringBuilder = new StringBuilder(); using XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, new XmlWriterSettings { Indent = true, IndentChars = " ", OmitXmlDeclaration = false }); doc.WriteTo(xmlWriter); xmlWriter.Flush(); string text = stringBuilder.ToString(); if (text.Length > 50000) { text = text.Substring(0, 50000) + "\n... (truncated)"; } return ToolResult.Ok(text); } }