根据条件选择要序列化的POCO

时间:2016-05-30 07:45:45

标签: c# json inheritance serialization

我想基于" meta"中的字段将json文档中的嵌套对象序列化为POCO(普通的旧c#对象)。该对象的区域,但我不知道如何做到这一点。

例如说我有以下文档:

{
  "id": 123,
  "type": "beer",
  "_source": {
     "name": "myBeer",
     "brewery": "myBrewery", 
     "address": "blah"
  }
}

使用此格式,源字段将映射到具有字段名称,啤酒厂和地址的另一个POCO。

现在说我在同一个索引中有另一个doc,其中包含以下字段:

{
  "id": 345,
  "type": "brewery",
  "_source": {
    "name": "mybrewery",
    "date": "somedate", 
    "city": "somecity"
  }
}

所以根据文档的类型变量,_source信息应序列化为不同的POCO。

我知道ShouldSerialize方法适用于类中的属性,那么是否有类似于类的方法?基本上会根据条件将json对象序列化为特定的POCO?

2 个答案:

答案 0 :(得分:1)

您需要存储合同信息以从外部元信息中解析正确的类型。

编写一个转换器类,它将为您完成整个工作:

public class DocConverter
{
    private readonly Dictionary<string, Func<MetaInformation, object>> deserializers = new Dictionary<string, Func<MetaInformation, object>>();
    private readonly Dictionary<Type, Func<object, string>> serializers = new Dictionary<Type, Func<object, string>>();

    private class MetaInformation
    {
        [Newtonsoft.Json.JsonProperty( "id" )]
        public int Id { get; set; }
        [Newtonsoft.Json.JsonProperty( "type" )]
        public string Type { get; set; }
        [Newtonsoft.Json.JsonProperty( "_source" )]
        public object Source { get; set; }
    }

    public void Register<Source, SourceData>( string contractName, Func<Source, Tuple<int,SourceData>> converter, Func<<Tuple<int, SourceData>, Source> reverter )
        where Source : class
        where SourceData : class
    {
        deserializers.Add( contractName,
            ( m ) =>
            {
                SourceData data = Newtonsoft.Json.JsonConvert.DeserializeObject<SourceData>( m.Source.ToString() );
                return reverter( Tuple.Create( m.Id, data ) );                    
            } );

        serializers.Add( typeof( Source ),
            ( o ) =>
            {   
                var data = converter( (Source) o );
                var meta = new MetaInformation { Id = data.Item1, Type = contractName, Source = data.Item2, };
                return Newtonsoft.Json.JsonConvert.SerializeObject( meta );
            } );
    }

    public string Serialize( object source )
    {
        return serializers[ source.GetType() ]( source );
    }

    public object Deserialize( string jsonData )
    {
        var meta = Newtonsoft.Json.JsonConvert.DeserializeObject<MetaInformation>( jsonData );
        return deserializers[ meta.Type ]( meta );
    }
}

对于序列化,我们需要定义内部数据的结构/契约

public class BeerSource
{
    [Newtonsoft.Json.JsonProperty( "name" )]
    public string Name { get; set; }
    [Newtonsoft.Json.JsonProperty( "brewery" )]
    public string Brewery { get; set; }
    [Newtonsoft.Json.JsonProperty( "address" )]
    public string Address { get; set; }
}

public class BrewerySource
{
    [Newtonsoft.Json.JsonProperty( "name" )]
    public string Name { get; set; }
    [Newtonsoft.Json.JsonProperty( "date" )]
    public string Date { get; set; }
    [Newtonsoft.Json.JsonProperty( "city" )]
    public string City { get; set; }
}

真正的课程

public class Beer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Brewery { get; set; }
    public string Address { get; set; }
}

public class Brewery
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Date { get; set; }
    public string City { get; set; }
}

毕竟,我们只注册合同名称,转换器和还原器

var conv = new DocConverter();
conv.Register<Beer, BeerSource>( 
    "beer", 
    ( o ) => Tuple.Create( o.Id, new BeerSource { Name = o.Name, Brewery = o.Brewery, Address = o.Address, } ), 
    ( t ) => new Beer { Id = t.Item1, Name = t.Item2.Name, Brewery = t.Item2.Brewery, Address = t.Item2.Address, } );
conv.Register<Brewery, BrewerySource>( 
    "brewery", 
    ( o ) => Tuple.Create( o.Id, new BrewerySource { Name = o.Name, Date = o.Date, City = o.City, } ), 
    ( t ) => new Brewery { Id = t.Item1, Name = t.Item2.Name, Date = t.Item2.Date, City = t.Item2.City, } );

现在我们使用它的占地面积很小

object source;
object result;
string jsonData;

source = new Beer { Id = 123, Name = "myBeer", Brewery = "myBrewery", Address = "blah", };
jsonData = conv.Serialize( source );
// check the JSON result
Console.WriteLine( jsonData );
result = conv.Deserialize( jsonData );
// check the result type
Console.WriteLine( result.GetType().ToString() );

source = new Brewery { Id = 456, Name = "myBrewery", Date = "somedate", City = "somecity", };
jsonData = conv.Serialize( source );
// check the JSON result
Console.WriteLine( jsonData );
result = conv.Deserialize( jsonData );
// check the result type
Console.WriteLine( result.GetType().ToString() );

答案 1 :(得分:0)

您可以在序列化继承的类型时实现此目的。序列化程序将自动将元属性添加到JSON文档,这将有助于反序列化为正确的类型。但是,您可能必须调整一些设置才能实现此目的。告诉我们您使用的是什么序列化程序,以便有人可以帮助您了解那些细节。

然而,无论如何你不应该这样做。这些元属性特定于所使用的序列化器。例如。 Web API 2的默认JSON序列化程序添加了一个名为“$type”的属性,而Data Contract JSON序列化程序添加了一个名为“__type”的属性。这意味着您在JSON提供程序和JSON使用者之间引入了紧密耦合 - 两者都被迫使用相同的序列化程序。

_source创建两种不同的类型:一种用于啤酒厂,另一种用于啤酒。这将为您和每个利益相关者节省很多麻烦。