Git Product home page Git Product logo

Comments (16)

elgonzo avatar elgonzo commented on July 19, 2024 1

I think i found the problem. It's the JsonNodeExtensions.GetInteger method in Json.More. Note that it misses a TryGetValue for uint values (it has ushort mistakenly twice):

public static long? GetInteger(this JsonValue value)
{
if (value.TryGetValue(out JsonElement e))
{
if (e.ValueKind != JsonValueKind.Number) return null;
var d = e.GetDecimal();
if (d == Math.Floor(d)) return (long)d;
return null;
}
if (value.TryGetValue(out byte b)) return b;
if (value.TryGetValue(out sbyte sb)) return sb;
if (value.TryGetValue(out short s)) return s;
if (value.TryGetValue(out ushort us)) return us;
if (value.TryGetValue(out int i)) return i;
if (value.TryGetValue(out ushort ui)) return ui;
if (value.TryGetValue(out long l)) return l;
// this doesn't feel right... throw?
if (value.TryGetValue(out ulong ul)) return (long)ul;
return null;
}

from json-everything.

ZarkoRunjevac avatar ZarkoRunjevac commented on July 19, 2024 1

I have updated Json.More.Net to 2.0.1. Issue is fixed. Thank you very much.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Can you please provide both the code that produces the JsonSchema object as well as as the json document that exhibits this issue? I did a quick test (obviously i had to make my own schema and json document), and i cannot reproduce the issue you describe with 6.0.3...

from json-everything.

ZarkoRunjevac avatar ZarkoRunjevac commented on July 19, 2024

Hi I can provide only snippets of code(general pattern):

public static readonly JsonSchema GenericSchema = new JsonSchemaBuilder()
    .Type(SchemaValueType.Object)
    .Schema(MetaSchemas.Draft7Id)
    .Description(
        "Configuration file.")
    .Properties(new Dictionary<string, JsonSchema>
    {
        {
            "genericId", new JsonSchemaBuilder() 
                .Type(SchemaValueType.String)
                .MinLength(11)
                .MaxLength(11)
                .Description("A unique identifier for the device and connected infrastructure.")
                .Build()
        },
        {
            "heartbeatId", new JsonSchemaBuilder() 
                .Type(SchemaValueType.String)
                .MinLength(11)
                .MaxLength(11)
                .Description("Heartbeat ID of the device.")
                .Build()
        },
        {
            "components", new JsonSchemaBuilder() 
                .Type(SchemaValueType.Array)
                .MinItems(1)
                .Items(new JsonSchemaBuilder().OneOf(
                    new JsonSchemaBuilder().Ref(new Uri("sunComponent", 
                        UriKind.RelativeOrAbsolute))))
        }
    })
    .Defs(new Dictionary<string, JsonSchema>
    {
        { "sunComponent", ComponentSchemas..SunComponent},
        { "componentSchema", ComponentSchemas.GenericComponentSchema },
        { "specificComponentSchema", SpecificComponentSchemas.SpecificComponentSchema },
        { "componentTypeSchema", ComponentSchemas.ComponentTypeSchema() },
        
    })
    .Required("genericId",
        "heartbeatId", "components")
    .AdditionalProperties(false);

Json document is created as described JsonSerializer.Serialize(MySchemaObject.Schema) and saved to file.

In 5.5.1 I to evaluate json I used only MySchemaObject.Schema.Evaluate(jsonDocumentInstance). After upgrade to 6.0.x it only works if if serialize schema object and then evaluate json.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Still not reproducible, unfortunately.

Your code example is still not complete (it lacks the definition of the SunComponent schema refered to by the "components" property. And the Uri used for the $ref is incorrect, considering that the SunComponent schema is in a $defs block. Both 5.5.1 and 6.0.3 do not accept this Uri with respect to the schema code you provided.

Fixing the sunComponent $ref Uri (from new Uri("sunComponent", UriKind.RelativeOrAbsolute) to new Uri("#/$defs/sunComponent", UriKind.RelativeOrAbsolute)) as well as inventing my own SunComponent schema, i observe 6.0.3 behaves the same as 5.5.1 with both evaluationResultFromSchema and directEvaluationResult results being valid.

I'd like to provide a dotnetfiddle that proves (or disproves, if i am unlucky) that 6.0.3 behaves like 5.5.1 with the code given, but dotnetfiddle currently times out when trying to fetch the JsonSchema.Net 6.0.3 package. Hrmpf...

Therefore, i'd suggest you inspect the (hierarchical) Details collection of the invalid evaluation result to see (A) which part of the schema and (B) which part of the validated json data is contributing to the invalid result. That should help you hone in on which parts of the schema and the validated json data are likely relevant to the issue you observe, which might be helpful to either identify the issue or provide guidance for you to produce a complete reproduction code and json data exhibiting the issue.

from json-everything.

ZarkoRunjevac avatar ZarkoRunjevac commented on July 19, 2024

Unfortunately I can't provide my real json definitions and json schema.

In the case when I evaluate schema directly:

var directEvaluationResult = MySchemaObject.Schema.Evaluate(jsonDocumentInstance);

I get [oneOf, Expected 1 matching subschema but found 0]

but when I execute same code using:

var schemaFromString = JsonSchema.FromText(JsonSerializer.Serialize(MySchemaObject.Schema));
var evaluationResultFromSchema = schemaFromString.Evaluate(jsonDocumentInstance);

evaluationResultFromSchema.IsValid is valid.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Unfortunately I can't provide my real json definitions and json schema.

Okay. Then try using the list output format, if you have not done so already, i.e.:

var evaluationResultFromSchema = schemaFromString.Evaluate(jsonDocumentInstance, new EvaluationOptions { OutputFormat = OutputFormat.List });
var directEvaluationResult = Schemas.GenericSchema.Evaluate(jsonDocumentInstance, new EvaluationOptions { OutputFormat = OutputFormat.List });

Crawl the Details collection of the evaluation result for any item that has its HasErrors property set to true. Of those items, take note of the InstanceLocation (that points to the part in the validated json data that fails validation) and SchemaLocation (pointing to the part of the (sub) schema that produces a failing validation). This should give you more context about which parts of your json data are failing to be validated by which parts of your schema(s). Hopefully this can help you honing in on the issue.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Just as an FYI:

This is the dotnetfiddle i was trying to provide: https://dotnetfiddle.net/1hQdMT (Update: dotnetfiddle is working again. Updated my dotnetfiddle link with the working fiddle...)

By the way, i did my tests with both the .net8 flavor (for 6.0.3) as well as the .netstandard20 flavor (for both 6.0.3 and 5.5.1) of JsonSchema.Net and the respective dependencies JsonSchema.Net relies on in a console application with .net8 as target framework. As said, i did not observe any difference between 5.5.1 and 6.0.3.

from json-everything.

gregsdennis avatar gregsdennis commented on July 19, 2024

Unfortunately I can't provide my real json definitions and json schema. - @ZarkoRunjevac

If you'd like to DM me in Slack, I may be able to help further. I keep everything confidential, and because it's a free workspace, it'll eventually be lost to the void.

from json-everything.

ZarkoRunjevac avatar ZarkoRunjevac commented on July 19, 2024

Hi, I managed to narrow down issue:
My test code is:
Schema and model definition:

public static class Schemas
{
    private static JsonSchema BuildingTypeIdSchema()
    {
        var enumValues = Enum.GetValues(typeof(BuildingTypeId))
            .Cast<BuildingTypeId>();

        var oneOfList = enumValues.Select(value => new JsonSchemaBuilder().Const((uint)value)
                .Title(value.ToString())
                .Build())
            .ToList();

        return new JsonSchemaBuilder()
            .OneOf(oneOfList)
            .AdditionalProperties(false)
            .Build();
    }

    private static JsonSchema BuildingTypeSchema()
    {
        var enumValues = Enum.GetValues(typeof(BuildingType))
            .Cast<BuildingType>();

        var oneOfList = enumValues.Select(value => new JsonSchemaBuilder().Const((uint)value)
                .Title(value.ToString())
                .Build())
            .ToList();

        return new JsonSchemaBuilder()
            .OneOf(oneOfList)
            .AdditionalProperties(false)
            .Build();
    }

    public static readonly JsonSchema BuildingSchema = new JsonSchemaBuilder()
        .Properties(new Dictionary<string, JsonSchema>
        {
            {
               "id", new JsonSchemaBuilder()
                    .Type(SchemaValueType.Integer)
                    .Build()
            },
            {
                "buildingType", new JsonSchemaBuilder()
                    .Type(SchemaValueType.Integer)
                    .Ref(new Uri("#/$defs/buildingType", UriKind.RelativeOrAbsolute))
            },
            {
                "buildingTypeId", new JsonSchemaBuilder()
                    .Type(SchemaValueType.Integer)
                    .Ref(new Uri("#/$defs/buildingTypeId", UriKind.RelativeOrAbsolute))
            }
        })
        .Defs(new Dictionary<string, JsonSchema>
        {
            { "buildingType", BuildingTypeSchema() },
            { "buildingTypeId", BuildingTypeIdSchema() },
        })
        .Required("id",
            "buildingType",
            "buildingTypeId")
        .AdditionalProperties(false)
        .Build();
}

public class Building
{
    [JsonPropertyName("id")] public uint Id { get; set; }
    [JsonPropertyName("buildingType")] public BuildingType BuildingType { get; set; }
    [JsonPropertyName("buildingTypeId")] public BuildingTypeId BuildingTypeId { get; set; }
}

public enum BuildingType : uint
{
    Type0 = 0,
    Type1 = 1,
    Type2 = 2,
    Type5 = 5,
    Type8 = 8,
    Type13 = 13,
    Type19 = 19,
    Type40 = 40,
    Type48 = 48
}

public enum BuildingTypeId : uint
{
    Current = 85,
    System = 112
}

Program.cs:

using System.Text.Json;
using Json.Schema;
using Schema;

var json = @"{
			""id"": 23,
			""buildingType"": 19,
			""buildingTypeId"": 85
			}";

var jsonDocumentInstance = JsonDocument.Parse(json);

var schemaText = JsonSerializer.Serialize(Schemas.BuildingSchema);
var schemaFromString = JsonSchema.FromText(schemaText);

var directEvaluationResult = Schemas.BuildingSchema.Evaluate(jsonDocumentInstance, new EvaluationOptions
{
	OutputFormat              = OutputFormat.List,
	ValidateAgainstMetaSchema = true,
	RequireFormatValidation   = true
});

var evaluationResultFromSchema = schemaFromString.Evaluate(jsonDocumentInstance, new EvaluationOptions
{
	OutputFormat              = OutputFormat.List,
	ValidateAgainstMetaSchema = true,
	RequireFormatValidation   = true
});
		
Console.WriteLine(directEvaluationResult.IsValid);
Console.WriteLine(evaluationResultFromSchema.IsValid);

When version 5.5.1 version is used directEvaluationResult and evaluationResultFromSchema are valid, but on 6.0.3
evaluationResultFromSchema is only valid.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Alright, i can reproduce. And i can dramatically simplify the repro code and narrow down to what triggers the bug. It appears the issue is caused by using uint values for building the const schemas in new JsonSchemaBuilder().Const((uint)value).

Simplified reproduction code that demonstrates the issue occurring with uint const values but not with int const values:

using System.Text.Json;
using System.Text.Json.Serialization;
using Json.Schema;


var schemaWithInt = new JsonSchemaBuilder()
    .Title("Schema with const Int")
    .Properties(("prop", new JsonSchemaBuilder().Const((int)1)))
    .AdditionalProperties(false)
    .Build();

var schemaWithUInt = new JsonSchemaBuilder()
    .Title("Schema with const UInt")
    .Properties(("prop", new JsonSchemaBuilder().Const((uint)1)))
    .AdditionalProperties(false)
    .Build();

TestSchema(schemaWithInt);
Console.WriteLine();
TestSchema(schemaWithUInt);


static void TestSchema(JsonSchema schema)
{
    var json = """{"prop":1}""";
    var jsonDocumentInstance = JsonDocument.Parse(json);


    var schemaText = JsonSerializer.Serialize(schema);
    var schemaFromString = JsonSchema.FromText(schemaText);

    var directEvaluationResult = schema.Evaluate(jsonDocumentInstance, new EvaluationOptions
    {
        OutputFormat = OutputFormat.List,
        ValidateAgainstMetaSchema = true,
        RequireFormatValidation = true
    });

    var evaluationResultFromSchema = schemaFromString.Evaluate(jsonDocumentInstance, new EvaluationOptions
    {
        OutputFormat = OutputFormat.List,
        ValidateAgainstMetaSchema = true,
        RequireFormatValidation = true
    });

    Console.WriteLine(schema.GetTitle());
    Console.WriteLine(directEvaluationResult.IsValid);
    Console.WriteLine(evaluationResultFromSchema.IsValid);
}

dotnetfiddle: https://dotnetfiddle.net/Gk2Ghy

from json-everything.

gregsdennis avatar gregsdennis commented on July 19, 2024

Thanks @elgonzo. One more thing to check is the JsonNode.IsEquivalentTo() extension method for ints and uints.

That's in Json.More.Net, so it may not be a schema issue.

from json-everything.

elgonzo avatar elgonzo commented on July 19, 2024

Also in the GetInteger method, the ulong handling is definitely incorrect too (as the code comment already implies):

// this doesn't feel right... throw?
if (value.TryGetValue(out ulong ul)) return (long)ul;

This will for example make schema validation fail exactly in the same manner as described by OP when building const schema instances using ulong values that exceed the value range of the long type. Repo code:

var schemaWithLargeULong = new JsonSchemaBuilder()
    .Title("Schema with large const ULong")
    .Properties(("prop", new JsonSchemaBuilder().Const(10_000_000_000_000_000_000)))
    .AdditionalProperties(false)
    .Build();

var json = """{"prop":10000000000000000000}""";
var jsonDocumentInstance = JsonDocument.Parse(json);

  var schemaText = JsonSerializer.Serialize(schema);
   var schemaFromString = JsonSchema.FromText(schemaText);

  var directEvaluationResult = schema.Evaluate(jsonDocumentInstance, new EvaluationOptions
  {
      OutputFormat = OutputFormat.List,
      ValidateAgainstMetaSchema = true,
      RequireFormatValidation = true
  });

  var evaluationResultFromSchema = schemaFromString.Evaluate(jsonDocumentInstance, new EvaluationOptions
  {
      OutputFormat = OutputFormat.List,
      ValidateAgainstMetaSchema = true,
      RequireFormatValidation = true
  });

  Console.WriteLine(schema.GetTitle());
  Console.WriteLine(directEvaluationResult.IsValid);
  Console.WriteLine(evaluationResultFromSchema.IsValid);

dotnetfiddle: https://dotnetfiddle.net/HxwJgb

Not sure what the best approach would be to address this ulong issue, though, since GetInteger is a public API. Perhaps adjusting the GetNumber method would be sufficient, placing a value.TryGetValue(out ulong) check before the call of GetInteger? Something like this:

public static decimal? GetNumber(this JsonValue value)
{
    if (value.TryGetValue(out JsonElement e))
    {
	    if (e.ValueKind != JsonValueKind.Number) return null;
	    return e.GetDecimal();
    }
    
    if (value.TryGetValue(out ulong ul)) return ul;
    
    var number = GetInteger(value);
    if (number != null) return number;
    ...

from json-everything.

gregsdennis avatar gregsdennis commented on July 19, 2024

Yeah, I didn't know what to do with ulong. I realize that my approach wasn't... great. I like the approach of checking first in .GetNumber(). For external callers, I expect a note in the XML comments and in the docs will have to do.

from json-everything.

gregsdennis avatar gregsdennis commented on July 19, 2024

I've added a test in the JsonSchema suite that replicates @elgonzo's example.

PR is ☝️

from json-everything.

gregsdennis avatar gregsdennis commented on July 19, 2024

@ZarkoRunjevac Json.More.Net 2.0.1 will be pushed out soon. Please update to that in your project to confirm the fix.

from json-everything.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.