Skip to content
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

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;
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant