Skip to content
New issue

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

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

Already on GitHub? # to your account

Comparing an object-generated JObject to a string generated JObject can result in the values not being equal #3026

Open
nickquednow opened this issue Feb 27, 2025 · 0 comments

Comments

@nickquednow
Copy link

When the object is converted to a JSON string, it appears that when the object gets converted into a JToken, the JValue._valueType is converted to an enum such as JTokenType.Guid or JTokenType.TimeSpan, but if you convert a string to a JObject, the values will be brought in as a string for the following types (these are the ones I found in my ad hock testing. there may be more):

  • JTokenType.Guid
  • JTokenType.Date
  • JTokenType.TimeSpan

This can cause issues when you are trying to convert a JSON string into a comparable type to compare with a native object that you cannot edit.

As for workarounds, there are not any to my knowledge (except to convert it to a native type and check that way, or implement a custom value checker).

Proposed solution (only affects the JValue class):

  1. Add a new configuration option for the comparison that allows for a "lazy type check" to ignore the JValue._valueType variable.
  2. convert both values to a string
  3. do a string comparison instead of an object value check.

(affects everywhere else):

  1. The value would have to be passed down through multiple classes:
    1. JArray.DeepEquals
    2. JConstructor.DeepEquals
    3. JObject.DeepEquals
    4. JProperty.DeepEquals
    5. JToken.DeepEquals (both sets of functions)
    6. JTokenEqualityComparer.Equals
    7. JValue.DeepEquals

Code Example:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace NewtonsoftError
{
    internal class DataNotEqualException : Exception { }
    internal class Data : IEquatable<Data>
    {
        public Guid Guid { get; set; } = Guid.Parse("7e8da577-9d21-4ec1-baa1-31f2eb0d66ed");
        public Uri Uri { get; set; } = new Uri("http://example.com");
        public TimeSpan TimeSpan { get; set; } = TimeSpan.FromSeconds(100);

        public bool Equals(Data? other)
        {
            if (other == null) return false;
            if (other.Guid != Guid) return false;
            if (!other.Uri.Equals(Uri)) return false;
            if (!other.TimeSpan.Equals(TimeSpan)) return false;
            return true;
        }
    }
    internal class Program2
    {
        public static int Main()
        {
            Data originalData = new Data();

            string originalDataJsonString = JsonConvert.SerializeObject(originalData);

            JObject originalJsonObject = JObject.FromObject(originalData);
            JObject originalStringJsonObject = JObject.Parse(originalDataJsonString);

            Data? originalDataFromString = JsonConvert.DeserializeObject<Data>(originalDataJsonString);

            if (originalDataFromString != null)
            {
                if(!originalData.Equals(originalDataFromString))
                {
                    throw new DataNotEqualException();
                }
            }

            if (!JToken.DeepEquals(originalJsonObject, originalStringJsonObject))
            {
                throw new DataNotEqualException();
            }

            return 0;
        }
    }
}
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant