简体   繁体   中英

Json nested List<T> not deserializing correctly

I have the following JSON scene description:

{
  "$type": "SceneHandler.SceneDescription, SceneHandler",
  "Camera": {
    "$type": "RenderDataStructures.Cameras.Camera, RenderDataStructures",
    "Origin": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": 3.0,
      "Y": 3.0,
      "Z": 2.0
    },
    "LowerLeftCorner": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": -0.9216876,
      "Y": -0.7480924,
      "Z": 0.6697793
    },
    "Horizontal": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": 2.5914667,
      "Y": 0.0,
      "Z": -2.5914667
    },
    "Vertical": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": -0.74809206,
      "Y": 1.4961841,
      "Z": -0.74809206
    },
    "U": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": 0.70710677,
      "Y": 0.0,
      "Z": -0.70710677
    },
    "V": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": -0.4082483,
      "Y": 0.8164966,
      "Z": -0.4082483
    },
    "W": {
      "$type": "System.Numerics.Vector3, System.Private.CoreLib",
      "X": 0.5773503,
      "Y": 0.5773503,
      "Z": 0.5773503
    },
    "LensRadius": 5E-05,
    "Rng": {
      "$type": "System.Random, System.Private.CoreLib"
    }
  },
  "SceneEntities": {
    "$type": "System.Collections.Generic.List`1[[RenderDataStructures.Shapes.IIntersectionTarget, RenderDataStructures]], System.Private.CoreLib",
    "$values": [
      {
        "$type": "RenderDataStructures.Shapes.Sphere, RenderDataStructures",
        "Material": {
          "$type": "RenderDataStructures.Materials.Diffuse, RenderDataStructures",
          "Albedo": {
            "$type": "System.Numerics.Vector3, System.Private.CoreLib",
            "X": 0.8,
            "Y": 0.3,
            "Z": 0.3
          }
        },
        "Center": {
          "$type": "System.Numerics.Vector3, System.Private.CoreLib",
          "X": 0.0,
          "Y": 0.0,
          "Z": -1.0
        },
        "Radius": 0.5,
        "RadiusSquared": 0.25
      },
      {
        "$type": "RenderDataStructures.Shapes.Sphere, RenderDataStructures",
        "Material": {
          "$type": "RenderDataStructures.Materials.Diffuse, RenderDataStructures",
          "Albedo": {
            "$type": "System.Numerics.Vector3, System.Private.CoreLib",
            "X": 0.8,
            "Y": 0.8,
            "Z": 0.0
          }
        },
        "Center": {
          "$type": "System.Numerics.Vector3, System.Private.CoreLib",
          "X": 0.0,
          "Y": -100.5,
          "Z": -1.0
        },
        "Radius": 100.0,
        "RadiusSquared": 10000.0
      },
      {
        "$type": "RenderDataStructures.Shapes.Sphere, RenderDataStructures",
        "Material": {
          "$type": "RenderDataStructures.Materials.Glossy, RenderDataStructures",
          "Albedo": {
            "$type": "System.Numerics.Vector3, System.Private.CoreLib",
            "X": 0.8,
            "Y": 0.6,
            "Z": 0.2
          },
          "Roughness": 0.3
        },
        "Center": {
          "$type": "System.Numerics.Vector3, System.Private.CoreLib",
          "X": 1.0,
          "Y": 0.0,
          "Z": -1.0
        },
        "Radius": 0.5,
        "RadiusSquared": 0.25
      },
      {
        "$type": "RenderDataStructures.Shapes.Sphere, RenderDataStructures",
        "Material": {
          "$type": "RenderDataStructures.Materials.Dielectric, RenderDataStructures",
          "Albedo": {
            "$type": "System.Numerics.Vector3, System.Private.CoreLib",
            "X": 0.95,
            "Y": 0.95,
            "Z": 0.95
          },
          "IndexOfRefraction": 1.33333,
          "Roughness": 0.0001,
          "Rng": {
            "$type": "System.Random, System.Private.CoreLib"
          }
        },
        "Center": {
          "$type": "System.Numerics.Vector3, System.Private.CoreLib",
          "X": -1.0,
          "Y": 0.0,
          "Z": -1.0
        },
        "Radius": 0.5,
        "RadiusSquared": 0.25
      },
      {
        "$type": "RenderDataStructures.Shapes.Sphere, RenderDataStructures",
        "Material": {
          "$type": "RenderDataStructures.Materials.Dielectric, RenderDataStructures",
          "Albedo": {
            "$type": "System.Numerics.Vector3, System.Private.CoreLib",
            "X": 0.95,
            "Y": 0.95,
            "Z": 0.95
          },
          "IndexOfRefraction": 1.33333,
          "Roughness": 0.0,
          "Rng": {
            "$type": "System.Random, System.Private.CoreLib"
          }
        },
        "Center": {
          "$type": "System.Numerics.Vector3, System.Private.CoreLib",
          "X": -1.0,
          "Y": 0.0,
          "Z": -1.0
        },
        "Radius": -0.495,
        "RadiusSquared": 0.24502501
      }
    ]
  }
}

which has been serialized from an object like this:

public class SceneDescription
    {
        public SceneDescription()
        {
            SceneEntities = new List<IIntersectionTarget>();
        }

        public Camera Camera { get; set; }

        public List<IIntersectionTarget> SceneEntities { get; set; }
    }

Camera serializes and deserializes just fine, but when the scene entities are deserialized, they all end up with null/all 0'd data for every field. No errors are thrown. What am I doing wrong?

Serialize method:

public static void ExportScene(SceneDescription p_scene, string p_outputPath)
        {
            var serializedScene = JsonConvert.SerializeObject(p_scene, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All,
                Formatting = Formatting.Indented
            });

            File.WriteAllLines(p_outputPath, new[] { serializedScene });
        }

Deserialize method:

public static SceneDescription LoadScene(string p_filePath)
        {
            var scene = File.ReadAllText(p_filePath);
            return JsonConvert.DeserializeObject<SceneDescription>(scene, new JsonSerializerSettings()
            {
                TypeNameHandling = TypeNameHandling.All
            });
        }

EDIT: Adding additional definitions

IIntersectionTarget:

public interface IIntersectionTarget
    {
        public IMaterial Material { get; }
        public bool WasHit(Ray p_ray, float p_tMin, float p_tMax, ref HitRecord p_record);
    }

Sphere:

public class Sphere : IIntersectionTarget
    {
        public Sphere(Vector3 p_center, float p_radius, IMaterial p_material)
        {
            Center = p_center;
            Radius = p_radius;
            RadiusSquared = Radius * Radius;
            Material = p_material;
        }

        public IMaterial Material { get; }
        public Vector3 Center { get; }
        public float Radius { get; }
        public float RadiusSquared {get;}

        public bool WasHit(Ray p_ray, float p_tMin, float p_tMax, ref HitRecord p_record)
        {
            var originCenter = p_ray.Origin - Center;

            var a = Vector3.Dot(p_ray.Direction, p_ray.Direction);
            var b = Vector3.Dot(originCenter, p_ray.Direction);
            var c = Vector3.Dot(originCenter, originCenter) - RadiusSquared;

            var discriminant = b * b - a * c;
            var sqrtDiscriminant = MathF.Sqrt(discriminant);

            if (discriminant > 0)
            {
                var temp = (-b - sqrtDiscriminant) / a;
                if (temp < p_tMax && temp > p_tMin)
                {
                    p_record.T = temp;
                    p_record.P = p_ray.PointAt(p_record.T);
                    p_record.Normal = (p_record.P - Center) / Radius;
                    p_record.Material = Material;
                    return true;
                }

                temp = (-b + sqrtDiscriminant) / a;
                if (temp < p_tMax && temp > p_tMin)
                {
                    p_record.T = temp;
                    p_record.P = p_ray.PointAt(p_record.T);
                    p_record.Material = Material;
                    p_record.Normal = (p_record.P - Center) / Radius;
                    p_record.Material = Material;
                    return true;
                }
            }
            return false;
        }

IMaterial:

public interface IMaterial
    {

        public bool Scatter(Ray p_rayIn, ref HitRecord p_record, out Vector3 p_attenuation, out Ray p_scattered);

        public static Vector3 GetRandomInUnitSphere()
        {
            var rng = new Random();
            var p = Vector3.Zero;
            do
            {
                p = 2.0f * new Vector3((float)rng.NextDouble(), (float)rng.NextDouble(), (float)rng.NextDouble()) - Vector3.One;
            }
            while (p.LengthSquared() >= 1.0f);

            return p;
        }
    }

Camera:

public class Camera
    {
        public Camera()
        {

        }

        [JsonConstructor]
        public Camera(Vector3 p_origin, Vector3 p_target, Vector3 p_upVector, float p_vFov, float p_aspect, float p_aperture, float p_focalDistance)
        {
            LensRadius = p_aperture / 2;
            var theta = p_vFov * MathF.PI / 180;
            var halfHeight = MathF.Tan(theta / 2);
            var halfWidth = p_aspect * halfHeight;
            Origin = p_origin;
            W = Vector3.Normalize(Origin - p_target);
            U = Vector3.Normalize(Vector3.Cross(p_upVector, W));
            V = Vector3.Cross(W, U);
            LowerLeftCorner = Origin - halfWidth * p_focalDistance * U - halfHeight * p_focalDistance * V - p_focalDistance * W;
            Horizontal = 2 * halfWidth * p_focalDistance * U;
            Vertical = 2 * halfHeight * p_focalDistance * V;

            Rng = new Random();
        }

        public static Camera CopyCamera(Camera p_camera)
        {
            var newCamera = new Camera();
            newCamera.LensRadius = p_camera.LensRadius;
            newCamera.Origin = p_camera.Origin;
            newCamera.LowerLeftCorner = p_camera.LowerLeftCorner;
            newCamera.Horizontal = p_camera.Horizontal;
            newCamera.Vertical = p_camera.Vertical;
            newCamera.U = p_camera.U;
            newCamera.V = p_camera.V;
            newCamera.W = p_camera.W;

            newCamera.Rng = new Random();

            return newCamera;
        }

        public Vector3 Origin { get; set; }
        public Vector3 LowerLeftCorner { get; set; }
        public Vector3 Horizontal { get; set; }
        public Vector3 Vertical { get; set; }
        public Vector3 U { get; set; }
        public Vector3 V { get; set; }
        public Vector3 W { get; set; }
        public float LensRadius { get; set; }

        [JsonIgnore]
        public Random Rng { get; set; }

        public Ray GetRay(float p_s, float p_t)
        {
            var rd = LensRadius * GetRandomInUnitDisk();
            var offset = U * rd.X + V * rd.Y;
            return new Ray(Origin + offset, LowerLeftCorner + p_s * Horizontal + p_t * Vertical - Origin - offset);
            //return new Ray(Origin, LowerLeftCorner + p_s * Horizontal + p_t * Vertical - Origin);
        }

        private Vector3 GetRandomInUnitDisk()
        {
            var p = Vector3.Zero;
            do
            {
                p = 2.0f * new Vector3((float)Rng.NextDouble(), (float)Rng.NextDouble(), 0) - new Vector3(1.0f, 1.0f, 0.0f);
            }
            while (Vector3.Dot(p, p) >= 1.0f);

            return p;
        }
    }

Initialization of the scene:

var parameters = new RenderParemeters(1280, 640, 64);

            var lookFrom = new Vector3(3.0f, 3.0f, 2.0f);
            var lookAt = new Vector3(0.0f, 0.0f, -1.0f);
            var distance = (lookFrom - lookAt).Length();

            var scene = new SceneDescription();

            scene.Camera = new Camera(lookFrom,
                                lookAt,
                                new Vector3(0.0f, 1.0f, 0.0f),
                                20.0f,
                                (float)parameters.XResolution / parameters.YResolution,
                                0.0001f,
                                distance);

            scene.SceneEntities.Add(new Sphere(new Vector3(0.0f, 0.0f, -1.0f), 0.5f, new Diffuse(new Vector3(0.8f, 0.3f, 0.3f))));
            scene.SceneEntities.Add(new Sphere(new Vector3(0.0f, -100.5f, -1.0f), 100.0f, new Diffuse(new Vector3(0.8f, 0.8f, 0.0f))));
            scene.SceneEntities.Add(new Sphere(new Vector3(1.0f, 0.0f, -1.0f), 0.5f, new Glossy(new Vector3(0.8f, 0.6f, 0.2f), 0.3f)));
            scene.SceneEntities.Add(new Sphere(new Vector3(-1.0f, 0.0f, -1.0f), 0.5f, new Dielectric(new Vector3(0.95f, 0.95f, 0.95f), 1.33333f, 0.0001f)));
            scene.SceneEntities.Add(new Sphere(new Vector3(-1.0f, 0.0f, -1.0f), -0.495f, new Dielectric(new Vector3(0.95f, 0.95f, 0.95f), 1.33333f, 0.0f)));

Properties of your Sphere class are readonly, you should do them like below ({get;set;}):

    public IMaterial Material { get;set; }
    public Vector3 Center { get; set; }
    public float Radius { get; set; }
    public float RadiusSquared {get; set;}

Alternatively you can make setter private but mark property with [JsonProperty] attribute:

   [JsonProperty] 
public float Radius {get; private set;}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM