将JSON对象/字符串数组解析为新的自定义类

时间:2018-01-27 13:16:57

标签: c# json entity-framework json.net entity-framework-core

我试图解析一些JSON数据,并将ultimatley存储在数据库中。

我在存储不是对象本身的字符串/值集合时遇到问题。

例如 - " callingCodes" &安培; " altSpellings"

我想将这些存储在一个SQL表中,该表将引用它们所属的国家。

这是JSON的一个例子:

{
   "name":"Puerto Rico",
   "topLevelDomain":[
      ".pr"
   ],
   "alpha2Code":"PR",
   "alpha3Code":"PRI",
   "callingCodes":[
      "1787",
      "1939"
   ],
   "capital":"San Juan",
   "altSpellings":[
      "PR",
      "Commonwealth of Puerto Rico",
      "Estado Libre Asociado de Puerto Rico"
   ],
   "region":"Americas",
   "subregion":"Caribbean",
   "population":3474182,
   "latlng":[
      18.25,
      -66.5
   ]
},

我最初使用http://json2csharp.com/

创建了一些C#类来表示数据

这个建议我将值存储为字符串列表,我做了:

 public List<string> CallingCodes { get; set; }

我现在想将数据存储在一个表中,所以我创建了一个类&#34; TopLevelDomain&#34;将数据存储/链接到父国家:

 public class CallingCode
    {
        public int ID { get; set; }
        public int CountryID { get; set; }
        public string Code{ get; set; }
    }

所以我改变了父母如下:

public ICollection<CallingCode> CallingCodes { get; set; }

是否可以将字符串值引导到&#34; Code&#34;我的新班的财产?

或者我是想把两个逻辑撬到一个?

是否有正确的JSON模型,并将这些模型手动重组为我的新DB / Entity Framework模型?

3 个答案:

答案 0 :(得分:2)

这是您从此类JSON获得的自动生成的类。这里棘手的一点是List原始类型。

public class RootObject
{
    public string name { get; set; }
    public List<string> topLevelDomain { get; set; }
    public string alpha2Code { get; set; }
    public string alpha3Code { get; set; }
    public List<string> callingCodes { get; set; }
    public string capital { get; set; }
    public List<string> altSpellings { get; set; }
    public string region { get; set; }
    public string subregion { get; set; }
    public int population { get; set; }
    public List<double> latlng { get; set; }
}

像PostgreSQL这样的某些数据库支持数组作为基本类型。如果您正在使用PostgreSQL,那么您可以将这些属性数组生成为原始类型,并将它们按原样存储在服务器上。

对于不支持数组的其他数据库,不能将原始值列表存储到数据库的单个列中。处理它的最简单方法是引入序列化并创建可以存储到服务器的单个字符串。因此,查看上面的类,对于public List<string> topLevelDomain属性,您可以按照以下方式重写它,

[NotMapped]
public List<string> topLevelDomain
{
    get => Deserialize(TopLevelDomainString);
    set => TopLevelDomainString = Serialize(value);
}
public string TopLevelDomainString { get; set; }

使用NotMapped属性,EF不会映射topLevelDomain属性。但TopLevelDomainString将保留到数据库,它将从topLevelDomain获取值。对于Serialize/Deserialize方法,您可以使用任何序列化方法。您可以直接使用JsonSerializer(因为您已经在使用JSON对象。或者您可以使用,组合字符串作为分隔符,并使用它从服务器拆分字符串。

从EF Core 2.1版本开始,您可以直接使用Value-Conversion功能提供func进行转换(本质上是上面的序列化代码)到EF和EF将在从/向服务器读取/保存数据时执行此操作。这将避免您必须创建其他CLR属性。

答案 1 :(得分:0)

这是您自动生成的课程:

    public class RootObject
    {
        public string name { get; set; }
        public List<string> topLevelDomain { get; set; }
        public string alpha2Code { get; set; }
        public string alpha3Code { get; set; }
        public List<string> callingCodes { get; set; }
        public string capital { get; set; }
        public List<string> altSpellings { get; set; }
        public string region { get; set; }
        public string subregion { get; set; }
        public int population { get; set; }
        public List<double> latlng { get; set; }
    }

让我们准备另一个简单的:

    public class MyRootObject
    {
        public MyRootObject(RootObject root)
        {
            Name = root.name;
            List<CallingCode> callingCodesConverted = new List<CallingCode>();

            foreach (string code in root.callingCodes)
            {
                CallingCode newCode = new CallingCode() { Code = code };
                callingCodesConverted.Add(newCode);
            }

            CallingCodes = callingCodesConverted;
        }

        public string Name { get; set; }

        public List<CallingCode> CallingCodes { get; set; }
    }

现在你可以先从json编码到类RootObject,然后根据它创建MyRootObject

        string path = @"D:\test.txt";

        var r = new StreamReader(path);
        var myJson = r.ReadToEnd();

        RootObject root = Json.Decode<RootObject>(myJson);

        MyRootObject myroot = new MyRootObject(root);

当然MyRootObject只是一个例子。

答案 2 :(得分:-1)

  

是否有正确的JSON模型和手动方式   将这些重组为我的新DB / Entity Framework Models?

有些人可能会在他们的代码中使用它,有些可能会比这更糟糕。但是,我个人喜欢使用models / dtos来获取我能够知道的数据。

  我试图将两个逻辑撬到一个?

是。但是,这取决于。强烈地键入/定义对象和所有或者每次都只有Ser / Deser。

您的数据是已知的(没有未知的属性或任何内容,为什么每次都要对其进行序列化和反序列化?)

解决方案1:如果您按原样使用JSON来创建数据库条目

<强>的entites

public class Country //assuming this is country data
{
    public int Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("alpha2Code")]
    public string Alpha2Code { get; set; }

    [JsonProperty("alpha3Code")]
    public string Alpha3Code { get; set; }

    [JsonProperty("capital")]
    public string Capital { get; set; }

    [JsonProperty("region")]
    public string Region { get; set; }

    [JsonProperty("subregion")]
    public string Subregion { get; set; }

    [JsonProperty("population")]
    public long Population { get; set; }

    [JsonProperty("topLevelDomain")]
    public virtual List<TopLevelDomain> TopLevelDomains { get; set; }

    [JsonProperty("callingCodes")]
    public virtual List<CallingCodes> CallingCodes { get; set; }

    [JsonProperty("altSpellings")]
    public virtual List<AltSpellings> AltSpellings { get; set; }

    [JsonProperty("latlng")]
    public virtual List<Coordinates> Coordinates { get; set; }
}

public class TopLevelDomain
{
    public int Id { get; set; }

    [ForeignKey("Country")]
    public int CountryId {get; set; }

    public virtual Country Country { get; set; }

    public string DomainName { get; set; }
}

public class CallingCodes
{
    public int Id { get; set; }

    [ForeignKey("Country")]
    public int CountryId {get; set; }

    public virtual Country Country { get; set; }

    public string Code { get; set;} // either store it as String
    //OR
    public long Code { get; set;}
}

public class AltSpellings
{
    public int Id { get; set; }

    [ForeignKey("Country")]
    public int CountryId {get; set; }

    public virtual Country Country { get; set; }

    public string AltSpelling { get; set; }
}

public class Coordinates
{
    public int Id { get; set; }

    [ForeignKey("Country")]
    public int CountryId {get; set; }

    public virtual Country Country { get; set; }

    public double Coordinates { get; set; } //again either as string or double, your wish. I would use double
}

像这样使用

//assuming using Newtonsoft

var myJson = ....assuming one Country;
var myJsonList = ...assuming List<Country>;

var country = JsonConvert.DeserializeObject<Country>(myJson);
var countries = JsonConvert.DeserializeObject<List<Country>>(myJson);

解决方案2:首先可以为一些小数据导致过多的表,但第一个解决方案是基于对象和键入的更多,所以这是另一个

<强>实体

public class Country //assuming this is country data
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Alpha2Code { get; set; }

    public string Alpha3Code { get; set; }

    public string Capital { get; set; }

    public string Region { get; set; }

    public string Subregion { get; set; }

    public long Population { get; set; }

    public string TopLevelDomains { get; set; }

    public string CallingCodes { get; set;}

    public string AltSpellings { get; set; }

    public double Longitude { get; set;}

    public double Latitude { get; set; }
}

<强>模型

public class CountryJson 
{
    public int Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("alpha2Code")]
    public string Alpha2Code { get; set; }

    [JsonProperty("alpha3Code")]
    public string Alpha3Code { get; set; }

    [JsonProperty("capital")]
    public string Capital { get; set; }

    [JsonProperty("region")]
    public string Region { get; set; }

    [JsonProperty("subregion")]
    public string Subregion { get; set; }

    [JsonProperty("population")]
    public long Population { get; set; }

    [JsonProperty("topLevelDomain")]
    public List<string> TopLevelDomains { get; set; }

    [JsonProperty("callingCodes")]
    public List<string> CallingCodes { get; set;}

    [JsonProperty("altSpellings")]
    public List<string> AltSpellings { get; set; }

    [JsonProperty("latlng")]
    public List<string> Latlng { get; set; }
}

像这样使用

//assuming using Newtonsoft

var myJson = ....assuming one Country;

var countryJson = JsonConvert.DeserializeObject<CountryJson>(myJson);
//Write a Mapper Or Manual Map like below
var countryEntity = new Country 
{
    Name = countryJson.Name,
    ...
    TopLevelDomains = JsonConvert.Serialize(countryJson.TopLevelDomains),
    CallingCodes = JsonConvert.Serialize(countryJson.CallingCodes),
    ...//same for all list (NOTE: YOU NEED TO DESERIALIZE IT WHEN YOU FETCH IT FROM DB
    Longitude = countryJson.Latlng.ElementAt(0),//assuming 0 is longi, 1 is lat
    Latitude = countryJson.Latlng.ElementAt(1)//you can do it like above as well as string if you want
}