Skip to content

Commit c1d533f

Browse files
committed
Fix: Newtonsoft to JSON converter
1 parent dc12615 commit c1d533f

12 files changed

Lines changed: 231 additions & 166 deletions

File tree

Contentstack.Utils.Tests/Helpers/NodeParser.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
1-
using Contentstack.Utils.Interfaces;
1+
using System.Text.Json;
2+
using System.Text.Json.Serialization;
3+
using Contentstack.Utils.Interfaces;
24
using Contentstack.Utils.Models;
35
using Contentstack.Utils.Tests.Constants;
46
using Contentstack.Utils.Tests.Mocks;
5-
using Newtonsoft.Json;
67

78
namespace Contentstack.Utils.Tests.Helpers
89
{
910
public class NodeParser
1011
{
1112
public static Node parse(string jsonNode)
1213
{
13-
JsonSerializerSettings SerializerSettings = new JsonSerializerSettings();
14-
JsonSerializer Serializer = JsonSerializer.Create(SerializerSettings);
15-
16-
return JsonConvert.DeserializeObject<Node>(jsonNode, SerializerSettings);
14+
// Remove trailing commas before closing brackets/braces
15+
string cleanedJson = System.Text.RegularExpressions.Regex.Replace(
16+
jsonNode,
17+
@",(\s*[}\]])",
18+
"$1"
19+
);
20+
return JsonSerializer.Deserialize<Node>(cleanedJson);
1721
}
1822
}
1923
public class GQLParser
2024
{
2125
public static GQLModel<T> parse<T>(string jsonNode, string embedConnection = null) where T: IEmbeddedObject
2226
{
2327
var data = JsonToHtmlConstants.KGQLModel(jsonNode, embedConnection);
24-
JsonSerializerSettings SerializerSettings = new JsonSerializerSettings();
25-
JsonSerializer Serializer = JsonSerializer.Create(SerializerSettings);
26-
return JsonConvert.DeserializeObject<GQLModel<T>>(data, SerializerSettings);
28+
// Remove trailing commas before closing brackets/braces
29+
string cleanedJson = System.Text.RegularExpressions.Regex.Replace(
30+
data,
31+
@",(\s*[}\]])",
32+
"$1"
33+
);
34+
return JsonSerializer.Deserialize<GQLModel<T>>(cleanedJson);
2735
}
2836
}
2937

Contentstack.Utils.Tests/Mocks/CustomRenderOptionMock.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,22 @@ public CustomRenderOptionMock(IEntryEmbedable entry) : base(entry)
1212
}
1313
public override string RenderNode(string nodeType, Node node, NodeChildrenCallBack callBack)
1414
{
15+
string GetAttrString(string key)
16+
{
17+
if (!node.attrs.ContainsKey(key) || node.attrs[key] == null) return "";
18+
var val = node.attrs[key];
19+
if (val is string s) return s;
20+
if (val is System.Text.Json.JsonElement je && je.ValueKind == System.Text.Json.JsonValueKind.String) return je.GetString();
21+
return val.ToString();
22+
}
1523
switch (nodeType)
1624
{
1725
case "a":
1826
if (node.attrs.ContainsKey("target"))
1927
{
20-
return $"<a href=\"{(string)node.attrs["url"]}\" target=\"{(string)node.attrs["target"]}\">{callBack(node.children)}</a>";
28+
return $"<a href=\"{GetAttrString("url")}\" target=\"{GetAttrString("target")}\">{callBack(node.children)}</a>";
2129
}
22-
return $"<a href=\"{(string)node.attrs["url"]}\">{callBack(node.children)}</a>";
30+
return $"<a href=\"{GetAttrString("url")}\">{callBack(node.children)}</a>";
2331
}
2432
return base.RenderNode(nodeType, node, callBack);
2533
}
Lines changed: 20 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,42 @@
11
using System;
2+
using System.Text.Json.Serialization;
23
using Contentstack.Utils.Converters;
34
using Contentstack.Utils.Interfaces;
45
using Contentstack.Utils.Models;
5-
using Newtonsoft.Json;
66

77
namespace Contentstack.Utils.Tests.Mocks
88
{
99
public class GQLModel<T> where T: IEmbeddedObject
1010
{
11-
[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
11+
[JsonConverter(typeof(RTEJsonConverter))]
1212
public JsonRTENodes<T> multiplerte { get; set; }
1313
public JsonRTENode<T> singlerte { get; set; }
1414
}
1515

16-
[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
16+
[JsonConverter(typeof(RTEJsonConverter))]
1717
public class EntryModel : IEmbeddedEntry
1818
{
19-
[JsonProperty("system.uid")]
20-
public string Uid
21-
{
22-
get;
23-
set;
24-
}
25-
[JsonProperty("system.content_type_uid")]
26-
public string ContentTypeUid
27-
{
28-
get;
29-
set;
30-
}
31-
[JsonProperty("title")]
32-
public string Title
33-
{
34-
get;
35-
set;
36-
}
19+
[JsonPropertyName("system.uid")]
20+
public string Uid { get; set; }
21+
[JsonPropertyName("system.content_type_uid")]
22+
public string ContentTypeUid { get; set; }
23+
[JsonPropertyName("title")]
24+
public string Title { get; set; }
3725
}
3826

39-
[Newtonsoft.Json.JsonConverter(typeof(RTEJsonConverter))]
27+
[JsonConverter(typeof(RTEJsonConverter))]
4028
public class AssetModel : IEmbeddedAsset
4129
{
42-
[JsonProperty("system.uid")]
43-
public string Uid
44-
{
45-
get;
46-
set;
47-
}
48-
[JsonProperty("system.content_type_uid")]
49-
public string ContentTypeUid
50-
{
51-
get;
52-
set;
53-
}
54-
[JsonProperty("title")]
55-
public string Title
56-
{
57-
get;
58-
set;
59-
}
60-
[JsonProperty("filename")]
61-
public string FileName
62-
{
63-
get;
64-
set;
65-
}
66-
[JsonProperty("url")]
67-
public string Url
68-
{
69-
get;
70-
set;
71-
}
30+
[JsonPropertyName("system.uid")]
31+
public string Uid { get; set; }
32+
[JsonPropertyName("system.content_type_uid")]
33+
public string ContentTypeUid { get; set; }
34+
[JsonPropertyName("title")]
35+
public string Title { get; set; }
36+
[JsonPropertyName("filename")]
37+
public string FileName { get; set; }
38+
[JsonPropertyName("url")]
39+
public string Url { get; set; }
7240
}
7341
}
7442

Contentstack.Utils/Contentstack.Utils.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@
4040
<ItemGroup>
4141
<PackageReference Include="HtmlAgilityPack" Version="1.11.46" />
4242
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
43+
<PackageReference Include="System.Text.Json" Version="9.0.5" />
4344
</ItemGroup>
4445
</Project>
Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,76 @@
11
using System;
2-
using Newtonsoft.Json;
3-
using Newtonsoft.Json.Linq;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
using System.Collections.Generic;
45
using Contentstack.Utils.Models;
56

67
namespace Contentstack.Utils.Converters
78
{
89
public class NodeJsonConverter : JsonConverter<Node>
910
{
10-
public override Node ReadJson(JsonReader reader, Type objectType, Node existingValue, bool hasExistingValue, JsonSerializer serializer)
11+
public override Node Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
1112
{
12-
Node node = null;
13-
JObject jObject = JObject.Load(reader);
14-
if (jObject["type"] == null)
13+
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
1514
{
16-
node = new TextNode();
17-
node.type = "text";
18-
}else
19-
{
20-
node = new Node();
15+
var root = jsonDoc.RootElement;
16+
Node node;
17+
if (!root.TryGetProperty("type", out _))
18+
{
19+
var textNode = new TextNode();
20+
textNode.type = "text";
21+
if (root.TryGetProperty("text", out var textProp))
22+
textNode.text = textProp.GetString() ?? string.Empty;
23+
if (root.TryGetProperty("bold", out var boldProp))
24+
textNode.bold = boldProp.GetBoolean();
25+
if (root.TryGetProperty("italic", out var italicProp))
26+
textNode.italic = italicProp.GetBoolean();
27+
if (root.TryGetProperty("underline", out var underlineProp))
28+
textNode.underline = underlineProp.GetBoolean();
29+
if (root.TryGetProperty("strikethrough", out var strikeProp))
30+
textNode.strikethrough = strikeProp.GetBoolean();
31+
if (root.TryGetProperty("inlineCode", out var inlineCodeProp))
32+
textNode.inlineCode = inlineCodeProp.GetBoolean();
33+
if (root.TryGetProperty("subscript", out var subscriptProp))
34+
textNode.subscript = subscriptProp.GetBoolean();
35+
if (root.TryGetProperty("superscript", out var superscriptProp))
36+
textNode.superscript = superscriptProp.GetBoolean();
37+
if (root.TryGetProperty("classname", out var classnameProp))
38+
textNode.classname = classnameProp.GetString();
39+
if (root.TryGetProperty("id", out var idProp))
40+
textNode.id = idProp.GetString();
41+
node = textNode;
42+
}
43+
else
44+
{
45+
node = new Node();
46+
}
47+
foreach (var prop in root.EnumerateObject())
48+
{
49+
switch (prop.Name)
50+
{
51+
case "type":
52+
node.type = prop.Value.GetString();
53+
break;
54+
case "attrs":
55+
node.attrs = JsonSerializer.Deserialize<IDictionary<string, object>>(prop.Value.GetRawText(), options);
56+
break;
57+
case "children":
58+
node.children = JsonSerializer.Deserialize<List<Node>>(prop.Value.GetRawText(), options);
59+
break;
60+
}
61+
}
62+
return node;
2163
}
22-
serializer.Populate(jObject.CreateReader(), node);
23-
return node;
2464
}
25-
26-
public override void WriteJson(JsonWriter writer, Node value, JsonSerializer serializer)
65+
public override void Write(Utf8JsonWriter writer, Node value, JsonSerializerOptions options)
2766
{
28-
67+
writer.WriteStartObject();
68+
writer.WriteString("type", value.type);
69+
writer.WritePropertyName("attrs");
70+
JsonSerializer.Serialize(writer, value.attrs, options);
71+
writer.WritePropertyName("children");
72+
JsonSerializer.Serialize(writer, value.children, options);
73+
writer.WriteEndObject();
2974
}
3075
}
3176
}
Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,83 @@
11
using System;
22
using System.Linq;
33
using System.Reflection;
4-
using Newtonsoft.Json;
5-
using Newtonsoft.Json.Linq;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
66

77
namespace Contentstack.Utils.Converters
88
{
9-
public class RTEJsonConverter : JsonConverter
9+
public class RTEJsonConverter : JsonConverter<object>
1010
{
11-
public override bool CanConvert(Type objectType)
11+
public override bool CanConvert(Type typeToConvert)
1212
{
13-
throw new NotImplementedException();
13+
return true;
1414
}
15-
16-
public override object ReadJson(JsonReader reader, Type objectType,
17-
object existingValue, JsonSerializer serializer)
15+
public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
1816
{
19-
JObject jo = JObject.Load(reader);
20-
object targetObj = Activator.CreateInstance(objectType);
21-
22-
foreach (PropertyInfo prop in objectType.GetProperties()
23-
.Where(p => p.CanRead && p.CanWrite))
17+
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
2418
{
25-
JsonPropertyAttribute att = prop.GetCustomAttributes(true)
26-
.OfType<JsonPropertyAttribute>()
27-
.FirstOrDefault();
28-
29-
string jsonPath = (att != null ? att.PropertyName : prop.Name);
30-
JToken token = jo.SelectToken(jsonPath);
31-
32-
if (token != null && token.Type != JTokenType.Null)
19+
var root = jsonDoc.RootElement;
20+
object targetObj = Activator.CreateInstance(typeToConvert);
21+
foreach (PropertyInfo prop in typeToConvert.GetProperties(BindingFlags.Public | BindingFlags.Instance))
3322
{
34-
object value = token.ToObject(prop.PropertyType, serializer);
35-
prop.SetValue(targetObj, value, null);
23+
var attr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
24+
string jsonPath = attr != null ? attr.Name : prop.Name;
25+
JsonElement token = root;
26+
bool found = false;
27+
// Support nested property names like 'system.uid'
28+
if (jsonPath.Contains("."))
29+
{
30+
var parts = jsonPath.Split('.');
31+
JsonElement current = root;
32+
foreach (var part in parts)
33+
{
34+
if (current.ValueKind == JsonValueKind.Object && current.TryGetProperty(part, out var next))
35+
{
36+
current = next;
37+
found = true;
38+
}
39+
else
40+
{
41+
found = false;
42+
break;
43+
}
44+
}
45+
if (found)
46+
token = current;
47+
}
48+
else if (root.TryGetProperty(jsonPath, out var directToken))
49+
{
50+
token = directToken;
51+
found = true;
52+
}
53+
if (found)
54+
{
55+
object value = JsonSerializer.Deserialize(token.GetRawText(), prop.PropertyType, options);
56+
prop.SetValue(targetObj, value);
57+
}
58+
else
59+
{
60+
// Set default value for missing properties
61+
if (prop.PropertyType.IsValueType)
62+
prop.SetValue(targetObj, Activator.CreateInstance(prop.PropertyType));
63+
else
64+
prop.SetValue(targetObj, null);
65+
}
3666
}
67+
return targetObj;
3768
}
38-
39-
return targetObj;
4069
}
41-
42-
43-
public override void WriteJson(JsonWriter writer, object value,
44-
JsonSerializer serializer)
70+
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
4571
{
46-
72+
writer.WriteStartObject();
73+
foreach (PropertyInfo prop in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
74+
{
75+
var attr = prop.GetCustomAttribute<JsonPropertyNameAttribute>();
76+
string jsonPath = attr != null ? attr.Name : prop.Name;
77+
writer.WritePropertyName(jsonPath);
78+
JsonSerializer.Serialize(writer, prop.GetValue(value), options);
79+
}
80+
writer.WriteEndObject();
4781
}
4882
}
4983
}

0 commit comments

Comments
 (0)