C# 无法将Json反序列化为类型,找不到构造函数

C# 无法将Json反序列化为类型,找不到构造函数,c#,json,azure,json.net,seal,C#,Json,Azure,Json.net,Seal,我在反序列化发送到azure函数的JSON时遇到了一点问题。首先,我打算向azure发送一个带有post的密文类型数组,反序列化JSON以恢复我的数据,然后对该数据进行操作。如下所示,我的类被称为sampleClass,它有一个类型为的属性ciphertext: 这就是我试图序列化/反序列化到的类 要发布数据,我正在使用HttpClient,并将其发布为JSON,如图所示: HttpResponseMessage response = await client.PostAsJsonAsync("

我在反序列化发送到azure函数的JSON时遇到了一点问题。首先,我打算向azure发送一个带有post的密文类型数组,反序列化JSON以恢复我的数据,然后对该数据进行操作。如下所示,我的类被称为
sampleClass
,它有一个类型为的属性
ciphertext

这就是我试图序列化/反序列化到的类

要发布数据,我正在使用HttpClient,并将其发布为JSON,如图所示:

HttpResponseMessage response = await client.PostAsJsonAsync("api/Function1", cipher);
在我的azure函数中,我试图将Json作为流读入,并将其反序列化到sampleClass[],但是这给我带来了一个错误

//Receive data from The Http PostRequest.
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

//De serialises to an object.
sampleClass[] array = JsonConvert.DeserializeObject<sampleClass[]>(requestBody);
//从Http POST请求接收数据。
string requestBody=等待新的StreamReader(req.Body).ReadToEndAsync();
//反序列化到对象。
sampleClass[]数组=JsonConvert.DeserializeObject(requestBody);
引发的错误如下所示:

执行“功能1”(失败,Id=1be7633e-6b6a-4626-98b7-8fec98eac633) [11/02/2020 15:50:48]System.Private.CoreLib:发生异常时 执行函数:Function1。Newtonsoft.Json:找不到 用于Microsoft.Research.SEAL.Ciphertext类型的构造函数。A. 类应该有一个默认构造函数,一个带有 参数或用JsonConstructor属性标记的构造函数。 路径“[0].密文.CoeffModCount”,第1行,位置32


当我尝试反序列化我的JSON时会抛出此错误,我如何解决此问题?

您在这里遇到了几个问题。让我们把它们整理一下

首先,该类型既没有无参数构造函数,也没有单个参数化构造函数,如图所示:

第一个构造函数的参数是可选的,但这并不意味着它是无参数的,它只是意味着编译器在代码中不存在该值时提供该值。但是当通过反射调用构造函数时(Json.NET就是这么做的),仍然需要提供一个值;有关详细信息,请参阅。此类型缺少真正的无参数构造函数是导致Newtonsoft.Json:无法找到用于Microsoft.Research.SEAL.Ciphertext类型的构造函数的原因。要抛出的异常

(在注释中指出,您的问题是
sampleClass
缺少默认构造函数,但该注释是错误的。)

由于无法修改
密文
,因此提供自己的构造方法的标准方法是如下所示:

然后在序列化和反序列化过程中使用转换器,如下所示:

var settings = new JsonSerializerSettings
{
    Converters = { new CiphertextConverter(GlobalContext.Context) },
};              
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
工作演示小提琴#2

注:

  • 只要它是公共的,就不需要用
    [jsonstructor]
    标记
    sampleClass
    的无参数构造函数

  • 通过测试,为
    Ciphertext
    生成的Base64字符串似乎相当长,每个
    Ciphertext
    大约有.5 MB。由于Json.NET在解析过程中完全具体化了每个字符串,因此它在处理如此庞大的字符串时并不是非常有效。如果您的体系结构超出了标准或经验,则需要重新评估您的体系结构

免责声明
我不是保安专业人员。我无法告诉您,通过网络发送序列化的
密文是否会泄漏信息。我也不能建议您如何为应用程序选择合适的
SEALContext
,甚至不能建议您在客户端和服务器端使用兼容的上下文是否可能泄漏信息。这个答案只解释了如何通过Json.NET序列化特定的SEAL对象。

错误似乎非常清楚。我认为你没有无参数构造函数。只需在SampleClass上标记
[JsonConstructor]
属性。它应该可以解决这个问题。正如错误所示:您无法将JSON反序列化为Microsoft.Research.SEAL.Ciphertext,因为该类没有无参数构造函数。你的问题是什么,通过将该类序列化为JSON,你到底想做什么?我是否应该用
[JsonConstructor]
标记该类,因为我的sampleClass中定义了一个无参数构造函数?@CodeCaster我正试图向azure发送一个加密数据数组,我想对这些数据进行操作,然后将加密的数组返回给客户端。我可能没有正确地执行此操作,如何在不序列化的情况下将此数据发送到azure?对于我的理解不足,我深表歉意,但您对如何修复或绕过此问题有何建议?提前谢谢。非常感谢您的回答!!非常感谢。投票赞成向问题作者明确说明在构建加密系统时需要小心@k、 狗如果这保护了一些重要的东西,并且您计划将其投入生产,请找一家信誉良好的安全公司来验证您的工作。不,这是一些大学工作,我试图了解其基础,这是概念证明,而不是保护有价值的数据。谢谢你的意见!
public class Ciphertext : NativeObject
{
    public Ciphertext(MemoryPoolHandle pool = null)
    {
        // Contents omitted
    }

    public Ciphertext(SEALContext context, MemoryPoolHandle pool = null)
    {
        // Contents omitted
    }

    // Additional constructors, methods and members omitted.
public class CiphertextConverter : CustomCreationConverter<Ciphertext>
{
    public override Ciphertext Create(Type objectType)
    {
        return new Ciphertext(); // Use the default value for the optional parameter
    }
}
var settings = new JsonSerializerSettings
{
    Converters = { new CiphertextConverter() },
};
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
public class CiphertextConverter : JsonConverter<Ciphertext>
{
    readonly SEALContext context;
    
    public CiphertextConverter(SEALContext context) => this.context = context ?? throw new ArgumentNullException(nameof(context));

    public override Ciphertext ReadJson(JsonReader reader, Type objectType, Ciphertext existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var data = serializer.Deserialize<byte []>(reader);
        if (data == null)
            return null;
        var cipherText = new Ciphertext();
        using (var stream = new MemoryStream(data))
            cipherText.Load(context, stream);
        return cipherText;
    }

    public override void WriteJson(JsonWriter writer, Ciphertext value, JsonSerializer serializer)
    {
        using (var stream = new MemoryStream())
        {
            value.Save(stream, ComprModeType.Deflate); // TODO: test to see whether Deflate gives better size vs speed performance in practice.
            writer.WriteValue(stream.ToArray());
        }
    }
}
var settings = new JsonSerializerSettings
{
    Converters = { new CiphertextConverter(GlobalContext.Context) },
};              
var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
public class TestClass
{
    [TestMethod]
    public void JsonNetSaveLoadTest()
    {
        Debug.WriteLine("Testing Json.NET");
        
        Func<Ciphertext, SEALContext, Ciphertext> roundtrip = (cipher, context) =>
        {
            var clientArray = new [] { new sampleClass { ciphertext = cipher } };

            var settings = new JsonSerializerSettings
            {
                Converters = { new CiphertextConverter(GlobalContext.Context) },
            };
            
            var requestBody = JsonConvert.SerializeObject(clientArray, settings);
            
            Debug.Write("   ");
            Debug.WriteLine(requestBody);
            Debug.WriteLine("   requestBody.Length={0}", requestBody.Length);
            
            var array = JsonConvert.DeserializeObject<sampleClass []>(requestBody, settings);
            
            Assert.IsTrue(array.Length == clientArray.Length);
            
            var reserializedJson = JsonConvert.SerializeObject(array, settings);
            
            Debug.Write("   ");
            Debug.WriteLine(reserializedJson);
            
            Assert.IsTrue(requestBody == reserializedJson);

            return array[0].ciphertext;
        };
        
        SaveLoadTest(roundtrip);
        
        Console.WriteLine("   passed.");
    }
    
    // Adapted from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/CiphertextTests.cs#L113
    [TestMethod]
    public void DirectSaveLoadTest()
    {
        Debug.WriteLine("Testing direct save and load:");
        
        Func<Ciphertext, SEALContext, Ciphertext> roundtrip = (cipher, context) =>
        {
            Ciphertext loaded = new Ciphertext();

            Assert.AreEqual(0ul, loaded.Size);
            Assert.AreEqual(0ul, loaded.PolyModulusDegree);
            Assert.AreEqual(0ul, loaded.CoeffModCount);

            using (MemoryStream mem = new MemoryStream())
            {
                cipher.Save(mem);

                mem.Seek(offset: 0, loc: SeekOrigin.Begin);

                loaded.Load(context, mem);
            }
            return loaded;
        };
        
        SaveLoadTest(roundtrip);
        
        Debug.WriteLine("   passed.");
    }
    
    // Adapted from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/CiphertextTests.cs#L113
    static void SaveLoadTest(Func<Ciphertext, SEALContext, Ciphertext> roundtrip)
    {
        SEALContext context = GlobalContext.Context;
        KeyGenerator keygen = new KeyGenerator(context);
        Encryptor encryptor = new Encryptor(context, keygen.PublicKey);
        Plaintext plain = new Plaintext("2x^3 + 4x^2 + 5x^1 + 6");
        Ciphertext cipher = new Ciphertext();

        encryptor.Encrypt(plain, cipher);

        Assert.AreEqual(2ul, cipher.Size);
        Assert.AreEqual(8192ul, cipher.PolyModulusDegree);
        Assert.AreEqual(4ul, cipher.CoeffModCount);

        var loaded = roundtrip(cipher, context);
        
        Assert.AreEqual(2ul, loaded.Size);
        Assert.AreEqual(8192ul, loaded.PolyModulusDegree);
        Assert.AreEqual(4ul, loaded.CoeffModCount);
        Assert.IsTrue(ValCheck.IsValidFor(loaded, context));

        ulong ulongCount = cipher.Size * cipher.PolyModulusDegree * cipher.CoeffModCount;
        for (ulong i = 0; i < ulongCount; i++)
        {
            Assert.AreEqual(cipher[i], loaded[i]);
        }
    }
}

static class GlobalContext
{
    // Copied from https://github.com/microsoft/SEAL/blob/master/dotnet/tests/GlobalContext.cs
    static GlobalContext()
    {
        EncryptionParameters encParams = new EncryptionParameters(SchemeType.BFV)
        {
            PolyModulusDegree = 8192,
            CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree: 8192)
        };
        encParams.SetPlainModulus(65537ul);
        BFVContext = new SEALContext(encParams);

        encParams = new EncryptionParameters(SchemeType.CKKS)
        {
            PolyModulusDegree = 8192,
            CoeffModulus = CoeffModulus.BFVDefault(polyModulusDegree: 8192)
        };
        CKKSContext = new SEALContext(encParams);
    }

    public static SEALContext BFVContext { get; private set; } = null;
    public static SEALContext CKKSContext { get; private set; } = null;
    
    public static SEALContext Context => BFVContext;
}