using AiAgent.Core.Helpers; using System; using System.Collections.Generic; using System.IO; using System.Text.Encodings.Web; using System.Text.Json; /// /// A utility class to parse structured article text in JSON format. /// public static class JsonHelpers { /// /// Parses a JSON string representing an article into a dictionary. /// The dictionary keys are the section titles (e.g., "Title", "Summary", section headings), /// and the values are the content of those sections. /// /// The string content of the JSON file. /// A dictionary mapping section titles to their content. public static Dictionary SectionParser(string jsonContent) { var articleData = new Dictionary(); if (string.IsNullOrWhiteSpace(jsonContent)) { Console.WriteLine("Warning: Input JSON content is empty or null."); return articleData; } // 2. The Solution: Configure the options var options = new JsonDocumentOptions // <-- Changed type { // This property directly solves your problem AllowTrailingCommas = true, // It's also good practice to skip comments, just in case CommentHandling = JsonCommentHandling.Skip // <-- Changed property name }; try { // Use JsonDocument for efficient, read-only parsing without needing to define a full class structure. using (JsonDocument doc = JsonDocument.Parse(jsonContent, options)) { JsonElement root = doc.RootElement; // Iterate over all top-level properties in the JSON object. foreach (JsonProperty property in root.EnumerateObject()) { switch (property.Name) { // Handle the "Section" array, which contains multiple headed sections. case "Section": if (property.Value.ValueKind == JsonValueKind.Array) { foreach (JsonElement sectionElement in property.Value.EnumerateArray()) { if (sectionElement.TryGetProperty("Heading", out JsonElement heading) && sectionElement.TryGetProperty("Content", out JsonElement content)) { articleData[heading.GetString() ?? ""] = content.GetString() ?? ""; } } } break; // Handle the "References" array by serializing it back to a string. case "References": if (property.Value.ValueKind == JsonValueKind.Array) { // Get the raw JSON text of the entire "References" array. articleData[property.Name] = property.Value.GetRawText(); } break; // Handle all other properties that have simple string values. default: if (property.Value.ValueKind == JsonValueKind.String) { articleData[property.Name] = property.Value.GetString() ?? ""; } break; } } } } catch (JsonException ex) { // Handle cases where the input string is not valid JSON. Console.WriteLine($"Error parsing JSON: {ex.Message}"); // Optionally, rethrow or handle the error as appropriate for your application. // throw; } return articleData; } /// /// Inserts a new section into an article's JSON structure after a specified existing section. /// /// The original JSON string of the article. /// The heading for the new section to be added. /// The content for the new section. /// The heading of the section after which the new section should be inserted. /// A new, pretty-printed JSON string with the section added, or the original JSON string if the target section isn't found. public static string InsertSectionAfter(string articleJson, string newSectionHeading, string newSectionContent, string insertAfterHeading) { var deserializeOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; try { Article? article = JsonSerializer.Deserialize
(articleJson, deserializeOptions); if (article?.Sections == null) { Console.WriteLine("Warning: Could not insert section because the article JSON is malformed or contains no sections."); return articleJson; } // Find the index of the section we want to insert after (case-insensitive search) int targetIndex = article.Sections.FindIndex(s => s.Heading?.Equals(insertAfterHeading, StringComparison.OrdinalIgnoreCase) ?? false); if (targetIndex == -1) { Console.WriteLine($"Warning: Section with heading '{insertAfterHeading}' not found. No changes made."); return articleJson; // Return the original if the target section doesn't exist } // Create the new section object var newSection = new Section { Heading = newSectionHeading, Content = newSectionContent }; // Insert the new section at the position immediately after the target article.Sections.Insert(targetIndex + 1, newSection); // Serialize the modified object back to a pretty-printed JSON string var serializeOptions = new JsonSerializerOptions { WriteIndented = true, // Use an encoder that doesn't escape characters like apostrophes for better readability Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; return JsonSerializer.Serialize(article, serializeOptions); } catch (JsonException ex) { Console.WriteLine($"Error: Invalid JSON provided. Details: {ex.Message}"); return articleJson; // Return original JSON on error } } /// /// Checks if a JsonElement (e.g., the root) contains all specified mandatory fields. /// /// The JsonElement to check. /// An enumerable of field names that must exist. /// A list of missing field names. The list is empty if validation succeeds. public static List CheckMandatoryFields(JsonElement root, IEnumerable mandatoryFields) { if (root.ValueKind != JsonValueKind.Object) { return mandatoryFields.ToList(); // Or throw an exception, as the root isn't an object } var missingFields = new List(); foreach (string field in mandatoryFields) { if (!root.TryGetProperty(field, out _)) { missingFields.Add(field); } } return missingFields; } /// /// Checks if all specified fields in a JsonElement have a value kind of 'String'. /// Assumes fields have already been checked for existence. /// /// The JsonElement to check. /// An enumerable of field names to type-check. /// A list of fields that do not have a String value. Empty if validation succeeds. public static List CheckFieldsAreString(JsonElement root, IEnumerable fieldsToCheck) { if (root.ValueKind != JsonValueKind.Object) { return fieldsToCheck.ToList(); } var nonStringFields = new List(); foreach (string field in fieldsToCheck) { // This check is safer, in case the field exists but is null if (root.TryGetProperty(field, out JsonElement property) && property.ValueKind != JsonValueKind.String) { nonStringFields.Add(field); } } return nonStringFields; } }