Вложенный список Json не десериализуется правильно

#c# #json #.net #json.net

#c# #json #.net #json.net

Вопрос:

У меня есть следующее описание сцены JSON:

 {
  "$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
      }
    ]
  }
}
  

который был сериализован из объекта, подобного этому:

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

        public Camera Camera { get; set; }

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

Камера сериализует и десериализует просто отлично, но когда объекты сцены десериализуются, все они в конечном итоге имеют нулевые / все 0’d данные для каждого поля. Ошибки не выдаются. Что я делаю не так?

Метод сериализации:

 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 });
        }
  

Метод десериализации:

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

РЕДАКТИРОВАТЬ: добавление дополнительных определений

IIntersectionTarget:

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

Сфера:

 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 amp;amp; 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 amp;amp; 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;
        }
    }
  

Камера:

 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;
        }
    }
  

Инициализация сцены:

 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)));
  

Комментарии:

1. File.WriteAllLines(p_outputPath, new[] { serializedScene }); File.WriteAllText доступно. learn.microsoft.com/en-us/dotnet/api /…

2. На самом деле, это не помогает решить проблему.

3. Имеет ли TypeNameHandling.All какое-либо значение? Я не помню, чтобы у меня были какие-либо проблемы с этим в прошлом.

4. Авто и все оба вызывают одну и ту же проблему

5. Рисунки. Вы можете добавить обработчик ошибок, посмотреть, не выскочит ли там что-нибудь. newtonsoft.com/json/help/html/SerializationErrorHandling.htm

Ответ №1:

Свойства вашего класса Sphere доступны только для чтения, вы должны сделать их, как показано ниже ({get;set;}):

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

В качестве альтернативы вы можете сделать сеттер закрытым, но пометить свойство атрибутом [JsonProperty]:

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