18using System.Collections.Generic;
22using Newtonsoft.Json.Linq;
27 internal class GPALElementConverter : JsonConverter
29 public override bool CanConvert(Type objectType)
31 return objectType == typeof(GPALElement) || objectType == typeof(List<GPALElement>);
34 public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
39 token = JToken.Load(reader);
43 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to load JSON token",
null, GPALObjectType.None, ex);
44 return objectType == typeof(List<GPALElement>) ?
new List<GPALElement>() : null;
47 if (token.Type == JTokenType.Array)
50 var jsonArray = (JArray)token;
51 var result =
new List<GPALElement>();
55 foreach (JToken maybeWrapped
in jsonArray)
57 JToken payload = maybeWrapped;
60 if (maybeWrapped.Type == JTokenType.Array && maybeWrapped.Children().Count() == 1)
62 payload = maybeWrapped.First;
66 GPALElement element =
null;
68 if (payload.Type == JTokenType.Object)
73 element = DeserializeSingleElement((JObject)payload, serializer);
77 GPAL.PublishSimpleEvent(Enums.GPALEventType.EXCEPTION,
78 "Normal object failed: " + ex.Message);
81 else if (payload.Type == JTokenType.String)
83 string raw = payload.Value<
string>();
84 if (
string.IsNullOrWhiteSpace(raw))
87 if (raw ==
"no elements found")
91 if (raw.StartsWith(
"{\"Attributes\":") && raw.Contains(
"\"tagName\":\"head\""))
93 GPAL.PublishSimpleEvent(Enums.GPALEventType.WARNING,
94 "Detected broken escaped <head> string — attempting surgical repair");
99 var parsed = JObject.Parse(raw);
100 element = DeserializeSingleElement(parsed, serializer);
102 catch (JsonReaderException)
105 int endIndex = raw.LastIndexOf(
"\"pageURL\":");
110 int safeEnd = raw.IndexOf(
"\"", endIndex + 10) + 1;
111 if (safeEnd > endIndex)
113 string cleaned = raw.Substring(0, safeEnd) +
"}}";
117 var fixedObj = JObject.Parse(cleaned);
118 element = DeserializeSingleElement(fixedObj, serializer);
120 catch (Exception ex2)
122 GPAL.PublishSimpleEvent(Enums.GPALEventType.WARNING,
123 "Repair parse failed: " + ex2.Message);
129 if (element ==
null || element.Attributes ==
null)
131 GPAL.PublishSimpleEvent(Enums.GPALEventType.WARNING,
132 "Repair failed — creating minimal head element with known fields");
134 element =
new GPALElement();
135 element.Attributes =
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
136 element.Attributes[
"tagName"] =
"head";
139 if (raw.Contains(
"\"xpath\":"))
141 var match = System.Text.RegularExpressions.Regex.Match(raw,
@"""xpath"":""([^""]+)""");
142 if (match.Success && match.Groups.Count > 1)
143 element.Attributes[
"xpath"] = match.Groups[1].Value;
146 if (raw.Contains(
"\"css\":"))
148 var match = System.Text.RegularExpressions.Regex.Match(raw,
@"""css"":""([^""]+)""");
149 if (match.Success && match.Groups.Count > 1)
150 element.Attributes[
"css"] = match.Groups[1].Value;
153 if (raw.Contains(
"\"fullXPath\":"))
155 var match = System.Text.RegularExpressions.Regex.Match(raw,
@"""fullXPath"":""([^""]+)""");
156 if (match.Success && match.Groups.Count > 1)
157 element.Attributes[
"fullXPath"] = match.Groups[1].Value;
164 else if (raw.TrimStart().StartsWith(
"{"))
169 var obj = JObject.Parse(raw);
170 element = DeserializeSingleElement(obj, serializer);
172 catch (JsonReaderException jex)
174 GPAL.PublishSimpleEvent(Enums.GPALEventType.WARNING,
175 "Non-head string failed parse: " + jex.Message);
181 if (element !=
null && element.Attributes !=
null && element.Attributes.Count > 0)
195 GPAL.PublishSimpleEvent(Enums.GPALEventType.WARNING,
196 "Dropped item — no valid element created");
202 else if (token.Type == JTokenType.Object)
207 var element = DeserializeSingleElement((JObject)token, serializer);
208 if (element !=
null && element.Attributes !=
null)
222 else if (token.Type == JTokenType.String)
227 var jsonString = token.Value<
string>();
228 if (jsonString ==
"no elements found")
230 return objectType == typeof(List<GPALElement>) ?
new List<GPALElement>() : null;
232 var parsedObject = JObject.Parse(jsonString);
233 var element = DeserializeSingleElement(parsedObject, serializer);
234 if (element !=
null && element.Attributes !=
null)
245 return objectType == typeof(List<GPALElement>) ?
new List<GPALElement>() : null;
249 return objectType == typeof(List<GPALElement>) ?
new List<GPALElement>() : null;
251 private GPALElement DeserializeSingleElement(JObject jsonObject, JsonSerializer serializer)
253 var gpalelement =
new GPALElement();
256 if (jsonObject[
"Attributes"] !=
null)
258 var attributes =
new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
259 var attributesJson = jsonObject[
"Attributes"] as JObject;
261 if (attributesJson !=
null)
263 foreach (var prop
in attributesJson.Properties())
267 switch (prop.Name.ToLower())
270 var location = prop.Value.ToObject<Point>(serializer);
271 attributes[
"location"] = location;
276 var size = prop.Value.ToObject<Size>(serializer);
277 attributes[
"size"] = size;
281 var boundingRect = prop.Value.ToObject<ClientRectangle>(serializer);
282 attributes[
"boundingRect"] = boundingRect;
286 var xpaths = prop.Value.ToObject<List<XPathInfo>>(serializer);
287 attributes[
"xpaths"] = xpaths.FindAll(x => x !=
null).ToArray();
291 attributes[prop.Name] = prop.Value.ToObject<
object>(serializer);
299 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to deserialize property [{prop.Name}]",
null, GPALObjectType.None, ex);
302 gpalelement.Attributes = attributes;
306 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
"Attributes property is not a valid JObject");
311 GPAL.PublishSimpleEvent(GPALEventType.WARNING,
"Attributes property is missing or null");
315 if (jsonObject[
"ElementHandle"] !=
null)
319 gpalelement.ElementHandle = jsonObject[
"ElementHandle"].ToObject<
string>(serializer);
324 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to deserialize ElementHandle",
null, GPALObjectType.None, ex);
331 public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
333 GPAL.PublishSimpleEvent(GPALEventType.ERROR,
"Serialization not implemented");
336 public override bool CanWrite =>
false;
339 internal class PointConverter : JsonConverter
341 public override bool CanConvert(Type objectType)
343 return objectType == typeof(Point);
346 public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
350 JToken token = JToken.Load(reader);
351 if (token.Type == JTokenType.Object)
353 var obj = (JObject)token;
354 int x = obj[
"X"]?.Value<
int>() ?? 0;
355 int y = obj[
"Y"]?.Value<
int>() ?? 0;
356 return new Point(x, y);
358 else if (token.Type == JTokenType.String)
360 string[] parts = token.Value<
string>().Split(
',');
361 if (parts.Length == 2 &&
int.TryParse(parts[0], out
int x) &&
int.TryParse(parts[1], out
int y))
363 return new Point(x, y);
366 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Point format: [{token}]");
371 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to deserialize Point",
null, GPALObjectType.None, ex);
376 public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
378 var point = (Point)value;
379 writer.WriteValue($
"{point.X},{point.Y}");
382 public override bool CanWrite =>
true;
385 internal class SizeConverter : JsonConverter
387 public override bool CanConvert(Type objectType)
389 return objectType == typeof(Size);
392 public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
396 JToken token = JToken.Load(reader);
397 if (token.Type == JTokenType.Object)
399 var obj = (JObject)token;
400 int width = obj[
"Width"]?.Value<
int>() ?? 0;
401 int height = obj[
"Height"]?.Value<
int>() ?? 0;
402 return new Size(width, height);
404 else if (token.Type == JTokenType.String)
406 string[] parts = token.Value<
string>().Split(
',');
407 if (parts.Length == 2 &&
int.TryParse(parts[0], out
int width) &&
int.TryParse(parts[1], out
int height))
409 return new Size(width, height);
412 GPAL.PublishSimpleEvent(GPALEventType.WARNING, $
"Invalid Size format: [{token}]");
417 GPAL.PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to deserialize Size",
null, GPALObjectType.None, ex);
422 public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
424 var size = (Size)value;
425 writer.WriteValue($
"{size.Width},{size.Height}");
428 public override bool CanWrite =>
true;