Skip to content

Fix: Newtonsoft to JSON converter #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions Contentstack.Utils.Tests/Helpers/NodeParser.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
using Contentstack.Utils.Interfaces;
using System.Text.Json;
using System.Text.Json.Serialization;
using Contentstack.Utils.Interfaces;
using Contentstack.Utils.Models;
using Contentstack.Utils.Tests.Constants;
using Contentstack.Utils.Tests.Mocks;
using Newtonsoft.Json;

namespace Contentstack.Utils.Tests.Helpers
{
public class NodeParser
{
public static Node parse(string jsonNode)
{
JsonSerializerSettings SerializerSettings = new JsonSerializerSettings();
JsonSerializer Serializer = JsonSerializer.Create(SerializerSettings);

return JsonConvert.DeserializeObject<Node>(jsonNode, SerializerSettings);
// Remove trailing commas before closing brackets/braces
string cleanedJson = System.Text.RegularExpressions.Regex.Replace(
jsonNode,
@",(\s*[}\]])",
"$1"
);
return JsonSerializer.Deserialize<Node>(cleanedJson);
}
}
public class GQLParser
{
public static GQLModel<T> parse<T>(string jsonNode, string embedConnection = null) where T: IEmbeddedObject
{
var data = JsonToHtmlConstants.KGQLModel(jsonNode, embedConnection);
JsonSerializerSettings SerializerSettings = new JsonSerializerSettings();
JsonSerializer Serializer = JsonSerializer.Create(SerializerSettings);
return JsonConvert.DeserializeObject<GQLModel<T>>(data, SerializerSettings);
// Remove trailing commas before closing brackets/braces
string cleanedJson = System.Text.RegularExpressions.Regex.Replace(
data,
@",(\s*[}\]])",
"$1"
);
return JsonSerializer.Deserialize<GQLModel<T>>(cleanedJson);
}
}

Expand Down
12 changes: 10 additions & 2 deletions Contentstack.Utils.Tests/Mocks/CustomRenderOptionMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,22 @@ public CustomRenderOptionMock(IEntryEmbedable entry) : base(entry)
}
public override string RenderNode(string nodeType, Node node, NodeChildrenCallBack callBack)
{
string GetAttrString(string key)
{
if (!node.attrs.ContainsKey(key) || node.attrs[key] == null) return "";
var val = node.attrs[key];
if (val is string s) return s;
if (val is System.Text.Json.JsonElement je && je.ValueKind == System.Text.Json.JsonValueKind.String) return je.GetString();
return val.ToString();
}
switch (nodeType)
{
case "a":
if (node.attrs.ContainsKey("target"))
{
return $"<a href=\"{(string)node.attrs["url"]}\" target=\"{(string)node.attrs["target"]}\">{callBack(node.children)}</a>";
return $"<a href=\"{GetAttrString("url")}\" target=\"{GetAttrString("target")}\">{callBack(node.children)}</a>";
}
return $"<a href=\"{(string)node.attrs["url"]}\">{callBack(node.children)}</a>";
return $"<a href=\"{GetAttrString("url")}\">{callBack(node.children)}</a>";
}
return base.RenderNode(nodeType, node, callBack);
}
Expand Down
72 changes: 20 additions & 52 deletions Contentstack.Utils.Tests/Mocks/GQLModel.cs
Original file line number Diff line number Diff line change
@@ -1,74 +1,42 @@
using System;
using System.Text.Json.Serialization;
using Contentstack.Utils.Converters;
using Contentstack.Utils.Interfaces;
using Contentstack.Utils.Models;
using Newtonsoft.Json;

namespace Contentstack.Utils.Tests.Mocks
{
public class GQLModel<T> where T: IEmbeddedObject
{
[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
[JsonConverter(typeof(RTEJsonConverter))]
public JsonRTENodes<T> multiplerte { get; set; }
public JsonRTENode<T> singlerte { get; set; }
}

[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
[JsonConverter(typeof(RTEJsonConverter))]
public class EntryModel : IEmbeddedEntry
{
[JsonProperty("system.uid")]
public string Uid
{
get;
set;
}
[JsonProperty("system.content_type_uid")]
public string ContentTypeUid
{
get;
set;
}
[JsonProperty("title")]
public string Title
{
get;
set;
}
[JsonPropertyName("system.uid")]
public string Uid { get; set; }
[JsonPropertyName("system.content_type_uid")]
public string ContentTypeUid { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; }
}

[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
[JsonConverter(typeof(RTEJsonConverter))]
public class AssetModel : IEmbeddedAsset
{
[JsonProperty("system.uid")]
public string Uid
{
get;
set;
}
[JsonProperty("system.content_type_uid")]
public string ContentTypeUid
{
get;
set;
}
[JsonProperty("title")]
public string Title
{
get;
set;
}
[JsonProperty("filename")]
public string FileName
{
get;
set;
}
[JsonProperty("url")]
public string Url
{
get;
set;
}
[JsonPropertyName("system.uid")]
public string Uid { get; set; }
[JsonPropertyName("system.content_type_uid")]
public string ContentTypeUid { get; set; }
[JsonPropertyName("title")]
public string Title { get; set; }
[JsonPropertyName("filename")]
public string FileName { get; set; }
[JsonPropertyName("url")]
public string Url { get; set; }
}
}

1 change: 1 addition & 0 deletions Contentstack.Utils/Contentstack.Utils.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@
<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="System.Text.Json" Version="9.0.5" />
</ItemGroup>
</Project>
77 changes: 61 additions & 16 deletions Contentstack.Utils/Converters/NodeJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,76 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Collections.Generic;
using Contentstack.Utils.Models;

namespace Contentstack.Utils.Converters
{
public class NodeJsonConverter : JsonConverter<Node>
{
public override Node ReadJson(JsonReader reader, Type objectType, Node existingValue, bool hasExistingValue, JsonSerializer serializer)
public override Node Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Node node = null;
JObject jObject = JObject.Load(reader);
if (jObject["type"] == null)
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
{
node = new TextNode();
node.type = "text";
}else
{
node = new Node();
var root = jsonDoc.RootElement;
Node node;
if (!root.TryGetProperty("type", out _))
{
var textNode = new TextNode();
textNode.type = "text";
if (root.TryGetProperty("text", out var textProp))
textNode.text = textProp.GetString() ?? string.Empty;
if (root.TryGetProperty("bold", out var boldProp))
textNode.bold = boldProp.GetBoolean();
if (root.TryGetProperty("italic", out var italicProp))
textNode.italic = italicProp.GetBoolean();
if (root.TryGetProperty("underline", out var underlineProp))
textNode.underline = underlineProp.GetBoolean();
if (root.TryGetProperty("strikethrough", out var strikeProp))
textNode.strikethrough = strikeProp.GetBoolean();
if (root.TryGetProperty("inlineCode", out var inlineCodeProp))
textNode.inlineCode = inlineCodeProp.GetBoolean();
if (root.TryGetProperty("subscript", out var subscriptProp))
textNode.subscript = subscriptProp.GetBoolean();
if (root.TryGetProperty("superscript", out var superscriptProp))
textNode.superscript = superscriptProp.GetBoolean();
if (root.TryGetProperty("classname", out var classnameProp))
textNode.classname = classnameProp.GetString();
if (root.TryGetProperty("id", out var idProp))
textNode.id = idProp.GetString();
node = textNode;
}
else
{
node = new Node();
}
foreach (var prop in root.EnumerateObject())
{
switch (prop.Name)
{
case "type":
node.type = prop.Value.GetString();
break;
case "attrs":
node.attrs = JsonSerializer.Deserialize<IDictionary<string, object>>(prop.Value.GetRawText(), options);
break;
case "children":
node.children = JsonSerializer.Deserialize<List<Node>>(prop.Value.GetRawText(), options);
break;
}
}
return node;
}
serializer.Populate(jObject.CreateReader(), node);
return node;
}

public override void WriteJson(JsonWriter writer, Node value, JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, Node value, JsonSerializerOptions options)
{

writer.WriteStartObject();
writer.WriteString("type", value.type);
writer.WritePropertyName("attrs");
JsonSerializer.Serialize(writer, value.attrs, options);
writer.WritePropertyName("children");
JsonSerializer.Serialize(writer, value.children, options);
writer.WriteEndObject();
}
}
}
94 changes: 64 additions & 30 deletions Contentstack.Utils/Converters/RTEJsonConverter.cs
Original file line number Diff line number Diff line change
@@ -1,49 +1,83 @@
using System;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Contentstack.Utils.Converters
{
public class RTEJsonConverter : JsonConverter
public class RTEJsonConverter : JsonConverter<object>
{
public override bool CanConvert(Type objectType)
public override bool CanConvert(Type typeToConvert)
{
throw new NotImplementedException();
return true;
}

public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
JObject jo = JObject.Load(reader);
object targetObj = Activator.CreateInstance(objectType);

foreach (PropertyInfo prop in objectType.GetProperties()
.Where(p => p.CanRead && p.CanWrite))
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
{
JsonPropertyAttribute att = prop.GetCustomAttributes(true)
.OfType<JsonPropertyAttribute>()
.FirstOrDefault();

string jsonPath = (att != null ? att.PropertyName : prop.Name);
JToken token = jo.SelectToken(jsonPath);

if (token != null && token.Type != JTokenType.Null)
var root = jsonDoc.RootElement;
object targetObj = Activator.CreateInstance(typeToConvert);
foreach (PropertyInfo prop in typeToConvert.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
object value = token.ToObject(prop.PropertyType, serializer);
prop.SetValue(targetObj, value, null);
var attr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
string jsonPath = attr != null ? attr.Name : prop.Name;
JsonElement token = root;
bool found = false;
// Support nested property names like 'system.uid'
if (jsonPath.Contains("."))
{
var parts = jsonPath.Split('.');
JsonElement current = root;
foreach (var part in parts)
{
if (current.ValueKind == JsonValueKind.Object && current.TryGetProperty(part, out var next))
{
current = next;
found = true;
}
else
{
found = false;
break;
}
}
if (found)
token = current;
}
else if (root.TryGetProperty(jsonPath, out var directToken))
{
token = directToken;
found = true;
}
if (found)
{
object value = JsonSerializer.Deserialize(token.GetRawText(), prop.PropertyType, options);
prop.SetValue(targetObj, value);
}
else
{
// Set default value for missing properties
if (prop.PropertyType.IsValueType)
prop.SetValue(targetObj, Activator.CreateInstance(prop.PropertyType));
else
prop.SetValue(targetObj, null);
}
}
return targetObj;
}

return targetObj;
}


public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{

writer.WriteStartObject();
foreach (PropertyInfo prop in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var attr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
string jsonPath = attr != null ? attr.Name : prop.Name;
writer.WritePropertyName(jsonPath);
JsonSerializer.Serialize(writer, prop.GetValue(value), options);
}
writer.WriteEndObject();
}
}
}
Loading
Loading