|
1 | | -using System; |
| 1 | +using System; |
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Linq; |
| 4 | +using System.Runtime.CompilerServices; |
| 5 | +using System.Text.Json; |
| 6 | +using System.Text.Json.Serialization; |
| 7 | +using System.Text.RegularExpressions; |
| 8 | +using FlaUI.Core; |
4 | 9 | using FlaUI.Core.AutomationElements; |
5 | 10 | using FlaUInspect.Core.Extensions; |
6 | 11 |
|
7 | 12 | namespace FlaUInspect.Core; |
8 | 13 |
|
9 | | -public static class JsonExporter |
10 | | -{ |
11 | | - public static Dictionary<string, object> CollectNodeData(AutomationElement element, HashSet<string>? options = null) |
| 14 | +public static class JsonExporter { |
| 15 | + public static ExportOptions[] DefaultOptions = new[] |
12 | 16 | { |
13 | | - var dict = new Dictionary<string, object>(); |
14 | | - // Default options if none provided |
15 | | - options ??= new HashSet<string> { "ControlType", "ClassName", "Name", "AutomationId" }; |
16 | | - |
17 | | - if (options.Contains("ControlType")) dict["ControlType"] = element.Properties.ControlType.ValueOrDefault.ToString(); |
18 | | - if (options.Contains("ClassName")) dict["ClassName"] = element.Properties.ClassName.ValueOrDefault; |
19 | | - if (options.Contains("Name")) dict["Name"] = element.Properties.Name.ValueOrDefault; |
20 | | - if (options.Contains("AutomationId")) dict["AutomationId"] = element.Properties.AutomationId.ValueOrDefault; |
21 | | - if (options.Contains("HelpText")) dict["HelpText"] = element.Properties.HelpText.ValueOrDefault; |
22 | | - if (options.Contains("BoundingRectangle")) dict["BoundingRectangle"] = element.Properties.BoundingRectangle.ValueOrDefault.ToString(); |
23 | | - if (options.Contains("ProcessId")) dict["ProcessId"] = element.Properties.ProcessId.ValueOrDefault; |
24 | | - if (options.Contains("IsEnabled")) dict["IsEnabled"] = element.Properties.IsEnabled.ValueOrDefault; |
25 | | - if (options.Contains("IsOffscreen")) dict["IsOffscreen"] = element.Properties.IsOffscreen.ValueOrDefault; |
| 17 | + ExportOptions.ControlType, |
| 18 | + ExportOptions.ClassName, |
| 19 | + ExportOptions.Name, |
| 20 | + ExportOptions.AutomationId |
| 21 | + }; |
| 22 | + public enum ExportOptions { |
| 23 | + ControlType, |
| 24 | + ClassName, |
| 25 | + Name, |
| 26 | + AutomationId, |
| 27 | + HelpText, |
| 28 | + BoundingRectangle, |
| 29 | + ProcessId, |
| 30 | + IsEnabled, |
| 31 | + IsOffscreen, |
| 32 | + Value, |
| 33 | + SupportedPatterns |
| 34 | + } |
| 35 | + public class NodeInfo { |
| 36 | + public string? ControlType { get; set; } |
| 37 | + public string? ClassName { get; set; } |
| 38 | + public string? Name { get; set; } |
| 39 | + public string? AutomationId { get; set; } |
| 40 | + public string? HelpText { get; set; } |
| 41 | + public string? BoundingRectangle { get; set; } |
| 42 | + public int? ProcessId { get; set; } |
| 43 | + public bool? IsEnabled { get; set; } |
| 44 | + public bool? IsOffscreen { get; set; } |
| 45 | + public string? Value { get; set; } |
| 46 | + public string[]? SupportedPatterns { get; set; } |
| 47 | + public List<NodeInfo>? Children { get; set; } |
| 48 | + } |
| 49 | + //"v => node.ControlType = v.ToString()" |
| 50 | + private static Regex LambdaToOptionKey = new(@"=>\s*node\.(?<optionKey>\w+)", |
| 51 | + RegexOptions.Compiled | RegexOptions.IgnoreCase); |
| 52 | + |
| 53 | + public static void SetPropertyOrNull<T>(HashSet<string> options, AutomationProperty<T> prop, Action<T> OnValue, [CallerArgumentExpression(nameof(OnValue))] string? OptionNameOverride = null) { |
| 54 | + try { |
| 55 | + var match = LambdaToOptionKey.Match(OptionNameOverride ?? string.Empty); |
| 56 | + if (match.Success) |
| 57 | + OptionNameOverride = match.Groups["optionKey"].Value; |
| 58 | + |
| 59 | + if (String.IsNullOrWhiteSpace(OptionNameOverride) || !options.Contains(OptionNameOverride)) |
| 60 | + return; |
| 61 | + |
| 62 | + if (prop.TryGetValue(out var val)) |
| 63 | + OnValue(val); |
| 64 | + } catch { } |
| 65 | + } |
| 66 | + public static string SerializeNodeInfo(NodeInfo node) { |
| 67 | + return JsonSerializer.Serialize(node, new JsonSerializerOptions { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }); |
| 68 | + |
| 69 | + } |
| 70 | + public static NodeInfo CollectNodeData(AutomationElement element, HashSet<string> options) { |
| 71 | + var node = new NodeInfo(); |
| 72 | + |
| 73 | + var props = element.Properties; |
| 74 | + |
| 75 | + SetPropertyOrNull(options, props.ControlType, v => node.ControlType = v.ToString()); |
| 76 | + SetPropertyOrNull(options, props.ClassName, v => node.ClassName = v); |
| 77 | + SetPropertyOrNull(options, props.Name, v => node.Name = v); |
| 78 | + SetPropertyOrNull(options, props.AutomationId, v => node.AutomationId = v); |
| 79 | + SetPropertyOrNull(options, props.HelpText, v => node.HelpText = v); |
| 80 | + SetPropertyOrNull(options, props.BoundingRectangle, v => node.BoundingRectangle = v.ToString()); |
| 81 | + SetPropertyOrNull(options, props.ProcessId, v => node.ProcessId = v); |
| 82 | + SetPropertyOrNull(options, props.IsEnabled, v => node.IsEnabled = v); |
| 83 | + SetPropertyOrNull(options, props.IsOffscreen, v => node.IsOffscreen = v); |
| 84 | + try { |
| 85 | + if (options.Contains("Value") && element.Patterns.Value.TryGetPattern(out var valuePattern)) { |
| 86 | + SetPropertyOrNull(options, valuePattern.Value, v => node.Value = v); |
| 87 | + } |
| 88 | + } catch { } |
26 | 89 |
|
27 | 90 | if (options.Contains("SupportedPatterns")) { |
28 | 91 | try { |
29 | 92 | var patterns = element.GetSupportedPatterns(); |
30 | | - dict["SupportedPatterns"] = patterns.Select(p => p.Name) |
| 93 | + node.SupportedPatterns = patterns.Select(p => p.Name) |
31 | 94 | .Where(n => n != "LegacyIAccessible" && n != "LegacyIAccessiblePattern") |
32 | 95 | .OrderBy(x => x).ToArray(); |
33 | 96 | } catch { } |
34 | 97 | } |
35 | 98 |
|
36 | | - var children = new List<Dictionary<string, object>>(); |
| 99 | + var children = new List<NodeInfo>(); |
37 | 100 | try { |
38 | 101 | foreach (var child in element.FindAllChildren()) { |
39 | 102 | children.Add(CollectNodeData(child, options)); |
40 | 103 | } |
41 | 104 | } catch { } // Ignore errors fetching children |
42 | 105 |
|
43 | 106 | if (children.Count > 0) { |
44 | | - dict["Children"] = children; |
| 107 | + node.Children = children; |
45 | 108 | } |
46 | 109 |
|
47 | | - return dict; |
| 110 | + return node; |
48 | 111 | } |
49 | 112 | } |
0 commit comments