如何将参数传递给非默认的构造函数? [英] How to pass arguments to a non-default constructor?

查看:288
本文介绍了如何将参数传递给非默认的构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我大致如下图:

 公共类Foo
{
   公共美孚(酒吧酒吧,字符串x,y字符串)
   {
       this.Bar =酒吧;
       this.X = X;
       this.Y = Y;
   }

   [JsonIgnore]
   公共酒吧酒吧{获得;私定; }

   公共字符串x {获得;私定; }
   公共序列Y {获得;私定; }
}

公共类酒吧
{
    公共酒吧(串Z)
    {
        this.Z = Z;
    }

    公共字符串z中{获得;私定; }
}
 

我想以某种方式类型酒吧的对象传递给Foo类型的构造函数反序列化过程,即:

  VAR栏=新的酒吧(世界,你好);
VAR X = JsonConvert.DeserializeObject<富>(fooJsonString,吧);
 

解决方案

下面是我的想法有关问题的解决方案:

的问题:

Json.Net定制反序列化API不是透明的,即影响了我的类层次结构。

其实这不是在发生问题的时候,你会有10-20个班在你的项目,但如果你有上千个班庞大的工程,你是不是特别高兴,你需要遵守的面向对象的设计使用JSON的事实。网络的要求。

Json.Net好与填充(初始化)创建之后POCO对象。但它不是在所有情况下的真理,有时你会得到你的对象里面的构造函数初始化。而为了让这初始化发生,你需要传递正确的论点。这些正确的参数既可以内部序列文本或者可以将它们已经创建并初始化之前一些时间。不幸的是反序列化过程Json.Net传递默认值的论点,他不明白,在我的情况下,它总是会引起ArgumentNullException。

解决办法:

下面是方法,允许使用任何一组参数无论是序列化和非序列化反序列化过程中真正的自定义对象的创建,主要的问题是,该方法次优的,它需要2个阶段每个对象序列化的需要定制反序列化,但它的工作原理,并允许反序列化对象,您需要的方式,所以这里有云:

首先,我们重新组装CustomCreationConverter类方法如下:

 公共类FactoryConverter< T> :Newtonsoft.Json.JsonConverter
{
    ///<总结>
    ///将对象的JSON再presentation。
    ///< /总结>
    ///< PARAM NAME =作家>的<看到CREF =JsonWriter/>写上述< /参数>
    ///<参数名称=值>该值小于/参数>
    ///< PARAM NAME =串行>将调用串行< /参数>
    公众覆盖无效WriteJson(JsonWriter作家,对象的值,JsonSerializer串行)
    {
        抛出新NotSupportedException异常(CustomCreationConverter应该只反序列化使用。);
    }

    ///<总结>
    ///读取对象的JSON重新presentation。
    ///< /总结>
    ///< PARAM NAME =读者>的<看到CREF =JsonReader/>从阅读< /参数>
    ///< PARAM NAME =的objectType>对象的类型< /参数>
    ///< PARAM NAME =existingValue>对象的现有值被读取< /参数>
    ///< PARAM NAME =串行>将调用串行< /参数>
    ///<返回>将对象值小于。/回报>
    公众覆盖对象ReadJson(JsonReader读者,类型的objectType,反对existingValue,JsonSerializer串行)
    {
        如果(reader.TokenType == JsonToken.Null)
            返回null;

        t值= CreateAndPopulate(的objectType,serializer.Deserialize<字典<字符串,字符串>>(读者));

        如果(价值== NULL)
            抛出新JsonSerializationException(不创建的对象。);

        返回值;
    }

    ///<总结>
    ///创建随后将填充由串行器中的对象。
    ///< /总结>
    ///< PARAM NAME =的objectType>对象的类型< /参数>
    ///<返回>< /回报>
    公共抽象牛逼CreateAndPopulate(类型的objectType,字典<字符串,字符串> jsonFields);

    ///<总结>
    ///确定此实例是否可以将指定的对象类型。
    ///< /总结>
    ///< PARAM NAME =的objectType>对象的类型< /参数>
    ///<返回>
    ///&其中c取代;真&所述; / c取代;如果这种情况下可以将指定的对象类型;否则,c为C>假< / c取代。
    ///< /回报>
    公众覆盖布尔CanConvert(类型的objectType)
    {
        返回的typeof(T).IsAssignableFrom(的objectType);
    }

    ///<总结>
    ///获取一个值,指示是否该<见CREF =JsonConverter/>可以写JSON。
    ///< /总结>
    ///<值GT;
    ///&其中c取代;真&所述; / c取代;如果该<见CREF =JsonConverter/>可以写JSON;否则,c为C>假< / c取代。
    ///< /值GT;
    公众覆盖布尔CanWrite
    {
        得到
        {
            返回false;
        }
    }
}
 

接下来,我们创建的工厂类,将创建富:

 公共类FooFactory:FactoryConverter<富>
{
    公共FooFactory(酒吧酒吧)
    {
        this.Bar =酒吧;
    }

    公共酒吧酒吧{获得;私定; }

    公众覆盖美孚创建(类型的objectType,字典<字符串,字符串>参数)
    {
        返回新的Foo(酒吧,参数[X],参数[Y]);
    }
}
 

下面是一个示例code:

  VAR栏=新的栏(BarObject);

VAR fooSrc =新富
(
    酒吧,
    A,B
);

VAR海峡= JsonConvert.SerializeObject(fooSrc);

无功富= JsonConvert.DeserializeObject<富>(STR,新FooFactory(巴));

Console.WriteLine(STR);
 

在此例中foo包含了我们需要传递给一个Foo构造函数反序列化过程的说法。

I have approximately the following picture:

public class Foo
{
   public Foo(Bar bar, String x, String y)
   {
       this.Bar = bar;
       this.X = x;
       this.Y = y;
   }

   [JsonIgnore]
   public Bar Bar { get; private set; }

   public String X { get; private set; }
   public String Y { get; private set; }
}

public class Bar
{
    public Bar(String z)
    {
        this.Z = z;
    }

    public String Z { get; private set; }
}

I want somehow to pass an object of type Bar to a constructor of type Foo during deserialization, i.e:

var bar = new Bar("Hello world");
var x = JsonConvert.DeserializeObject<Foo>(fooJsonString, bar);

解决方案

Here are my thoughts regarding problem solution:

The problem:

Json.Net's custom deserialization api is not transparent, i.e. affects my class hierarchy.

Actually it's not a problem in case when you have 10-20 classes in your project, though if you have huge project with thousands of classes, you are not particularly happy about the fact that you need comply your OOP design with Json.Net requirements.

Json.Net is good with POCO objects which are populated (initialized) after they are created. But it's not truth in all cases, sometimes you get your objects initialized inside constructor. And to make that initialization happen you need to pass 'correct' arguments. These 'correct' arguments can either be inside serialized text or they can be already created and initialized some time before. Unfortunately Json.Net during deserialization passes default values to arguments that he doesn't understand, and in my case it always causes ArgumentNullException.

The solution:

Here is approach that allows real custom object creation during deserialization using any set of arguments either serialized or non-serialized, the main problem is that the approach sub-optimal, it requires 2 phases of deserialization per object that requires custom deserialization, but it works and allows deserializing objects the way you need it, so here goes:

First we reassemble the CustomCreationConverter class the following way:

public class FactoryConverter<T> : Newtonsoft.Json.JsonConverter
{
    /// <summary>
    /// Writes the JSON representation of the object.
    /// </summary>
    /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
    /// <param name="value">The value.</param>
    /// <param name="serializer">The calling serializer.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException("CustomCreationConverter should only be used while deserializing.");
    }

    /// <summary>
    /// Reads the JSON representation of the object.
    /// </summary>
    /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
    /// <param name="objectType">Type of the object.</param>
    /// <param name="existingValue">The existing value of object being read.</param>
    /// <param name="serializer">The calling serializer.</param>
    /// <returns>The object value.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        T value = CreateAndPopulate(objectType, serializer.Deserialize<Dictionary<String, String>>(reader));

        if (value == null)
            throw new JsonSerializationException("No object created.");

        return value;
    }

    /// <summary>
    /// Creates an object which will then be populated by the serializer.
    /// </summary>
    /// <param name="objectType">Type of the object.</param>
    /// <returns></returns>
    public abstract T CreateAndPopulate(Type objectType, Dictionary<String, String> jsonFields);

    /// <summary>
    /// Determines whether this instance can convert the specified object type.
    /// </summary>
    /// <param name="objectType">Type of the object.</param>
    /// <returns>
    ///     <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
    /// </returns>
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    /// <summary>
    /// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
    /// </value>
    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }
}

Next we create the factory class that will create our Foo:

public class FooFactory : FactoryConverter<Foo>
{
    public FooFactory(Bar bar)
    {
        this.Bar = bar;
    }

    public Bar Bar { get; private set; }

    public override Foo Create(Type objectType, Dictionary<string, string> arguments)
    {
        return new Foo(Bar, arguments["X"], arguments["Y"]);
    }
}

Here is sample code:

var bar = new Bar("BarObject");

var fooSrc = new Foo
(
    bar,
    "A", "B"
);

var str = JsonConvert.SerializeObject(fooSrc);

var foo = JsonConvert.DeserializeObject<Foo>(str, new FooFactory(bar));

Console.WriteLine(str);

In this case foo contains an argument that we needed to pass to a Foo constructor during deserialization.

这篇关于如何将参数传递给非默认的构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆