question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Nullable readonly structs are not deserialized with the corresponding constructor call

See original GitHub issue

We’ve successfully used ExtendedXmlSerializer in one of our projects with quite a complicated object graph. During testing, I found another issue that might be a bug. Please consider the following minimal, complete, verifiable example (MCVE):

public sealed class NullableReadonlyStructSerializationTests
{
    private readonly IExtendedXmlSerializer _serializer =
        new ConfigurationContainer().UseOptimizedNamespaces()
                                    .EnableParameterizedContentWithPropertyAssignments()
                                    .UseAutoFormatting()
                                    .Create();
                                        
    [Fact]
    public void SerializeAndDeserializeInstanceA()
    {
        var a = new A { Point = new Point(2, 3)} ;

        var xml = _serializer.Serialize(a);
        var deserializedA = _serializer.Deserialize<A>(xml);

        Assert.Equal(a.Point, deserializedA.Point); // This test works fine!
    }

    [Fact]
    public void SerializeAndDeserializeInstanceB()
    {
        var b = new B { NullablePoint = new Point(24, 7)};

        var xml = _serializer.Serialize(b);
        var deserializedB = _serializer.Deserialize<B>(xml);

        Assert.Equal(b.NullablePoint, deserializedB.NullablePoint);
        // This test breaks because the deserialized point is (0,0) instead of (24, 7)
    }
}

public sealed class A
{
    public Point Point { get; set; }
}

public sealed class B
{
    public Point? NullablePoint { get; set; }
}

public readonly struct Point : IEquatable<Point>
{
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public int X { get; }

    public int Y { get; }

    public bool Equals(Point other) => X == other.X && Y == other.Y;

    public override bool Equals(object? obj) => obj is Point other && Equals(other);

    public override int GetHashCode()
    {
        unchecked
        {
            return (X * 397) ^ Y;
        }
    }

    public override string ToString() => $"({X}, {Y})";

    public static bool operator ==(Point left, Point right) => left.Equals(right);

    public static bool operator !=(Point left, Point right) => !left.Equals(right);
}

In the example above, Point is implemented as an immutable struct. When I use this struct as a regular property in a class (as can be seen in class A), everything works fine. When I use it as a Point?, then the corresponding value is deserialized as (0,0) instead of (24, 7). On the IExtendedXmlSerializer instance, EnableParameterizedContentWithPropertyAssignments is configured among some other settings (but I don’t think they are the cause of this behavior).

I suspect that when the XML is deserialized as Point?, the default object initializer of the struct is called instead of the constructor that takes two arguments. Might this be the case? Or did I miss something when configuring the serializer?

Thank you so much for your help in advance!

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:16 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
issue-label-bot[bot]commented, Nov 20, 2020

Issue-Label Bot is automatically applying the label bug to this issue, with a confidence of 0.77. Please mark this comment with 👍 or 👎 to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

1reaction
feO2xcommented, Nov 23, 2020

@Mike-E-angelo I can now confirm that version 3.4.2.1-xufywhoa is fixing this issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I deserialize a readonly struct with a constructor ...
I have a readonly struct (Outer) that has a readonly struct ... as an in parameter to avoid one copy when the constructor...
Read more >
NullReferenceException when deserializing object ...
Calling JsonSerializer.Deserialize for an object containing a nullable struct property will throw a NullReferenceException when passing ...
Read more >
Migrate from Newtonsoft.Json to System.Text.Json - .NET
Deserialize JSON null literal to non-nullable value types ... lets you specify which constructor to call when deserializing to a POCO.
Read more >
How to write custom converters for JSON serialization - .NET
Json , see How to serialize and deserialize JSON in .NET. A converter is a class that converts an object or a value...
Read more >
Fields that are only assigned in the constructor should be " ...
readonly fields can only be assigned in a class constructor. If a class has a field that's not marked readonly but is only...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found