-1

I have encountered a rather weird issue while trying to deserialize a simple json into an object of type D3Point, which is used from a NuGet package.

The json looks like this:

string cJson = face["Edges"][0]["Coords"][0].ToString();
"{\"X\": 1262.6051066219518, \"Y\": -25972.229375190014, \"Z\": -299.99999999999994}"

And the deserialization attempt:

D3Point coord = JsonConvert.DeserializeObject<D3Point>(cJson);

After the above, coord's values are: {0;0;0}.

Below is the D3Point class.

public readonly struct D3Point : IEquatable<D3Point>
{
  public static readonly D3Point Null = new D3Point(0, 0, 0);

  public double X { get; }
  public double Y { get; }
  public double Z { get; }

  public D3Point(double coordinateX, double coordinateY, double coordinateZ)
  {
      this.x = coordinateX; 
      this.y = coordinateY;
      this.z = coordinateZ;
  }
}

What could be the problem and how would I be able to fix it?

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
Quest
  • 43
  • 6
  • your class (which is a struct, not a class, btw), does not compile. your constructor needs a body. – Franz Gleichmann Jul 30 '21 at 09:50
  • It does, I just haven't included its body. – Quest Jul 30 '21 at 09:52
  • @FranzGleichmann that's an external type – Marc Gravell Jul 30 '21 at 09:52
  • Presumably json.net is having trouble matching the json key `X` to the constructor parameter `coordinateX` – canton7 Jul 30 '21 at 09:53
  • 6
    The *ultimate* problem here is that the serializer can't write to those get-only properties, and hasn't figured out to use the constructor - quite likely because the parameter names don't match the property names in an obvious way; if it was me, I'd use an intermediate DTO type that I control - a class that has get-set properties, and map to the custom struct afterwards – Marc Gravell Jul 30 '21 at 09:53
  • 1
    (The ctor body that you've posted won't compile either -- please make sure you post your *actual* code) – canton7 Jul 30 '21 at 09:53
  • Please comment out constructor and check – Vaibhav Deshmukh Jul 30 '21 at 09:55
  • @MarcGravell is correct. And forcing it using an attribute is not possible with 3rd party code either. Maybe this helps: https://stackoverflow.com/questions/23017716/json-net-how-to-deserialize-without-using-the-default-constructor Therefore I think this *might* be a duplicate after all – Sascha Jul 30 '21 at 09:56
  • I have updated the original post with the actual class. Sorry for the inconvenience. @MarcGravell thank you very much for your comment, I will follow your advice and see if I can sort it out. I have changed the json's coordinates from, for ex. "X" to "coordinateX", following canton7's advice but it still does not work. I will, however, try it out with a DTO type now. – Quest Jul 30 '21 at 09:57
  • @VaibhavDeshmukh I do not have access in `D3Point` as I got it from a NuGet package – Quest Jul 30 '21 at 09:58
  • @Sascha not really a duplicate I suppose as, in my case, I can not add attributes to the members inside `D3Point`, and most solutions in that post are involving changes in those classes – Quest Jul 30 '21 at 09:59
  • 1
    @Radu while deserialize create a new class with X, Y, Z properties and then assign it to D3Point – Vaibhav Deshmukh Jul 30 '21 at 10:02
  • Your constructor has errors, btw. `this.x` etc do not exist – Caius Jard Jul 30 '21 at 10:56
  • 1
    you can use `init;` property with C# 9.0. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/init – Akshay G Jul 30 '21 at 11:18

1 Answers1

0

You could just interim via a Dictionary<string, double>

//local or method
D3Point toD3Point(string json) { 
  var j = JsonConvert.DeserializeObject<Dictionary<string, double>>(json); 
  return new D3Point(j["X"],j["Y"],j["Z"]);
}
        
D3Point coord = toD3Point(cJson);

If you really wanted to one-line it, using LINQ is a bit nasty, but..

new[]{ JsonConvert.DeserializeObject<Dictionary<string, double>>(cJson) }.Select(j => new D3Point(j["X"],j["Y"],j["Z"]).First();
Caius Jard
  • 72,509
  • 5
  • 49
  • 80