Parse JSON Object / Array of Strings into new Custom Class

c# entity-framework entity-framework-core json json.net

Question

I'm trying to parse some JSON data and ultimatley store it in a database.

I'm having issues when storing a collection of strings / values which are not objects themselves.

For example - The "callingCodes" & "altSpellings"

I want to store these in an SQL table which will have reference to the country they belong to.

This is an example of the 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
   ]
},

I originally created some C# classes to represent the data using http://json2csharp.com/

This sugguested I store the values as a list of strings, which I did:

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

I now want to store the data in a table, so I created a class "TopLevelDomain" to store / link the data to the parent country:

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

So I altered the parent to be as follows:

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

Is it possible to direct the string values into the "Code" property of my new class?

Or am I trying to crowbar two pieces of logic into one?

Is the correct way to have models for the JSON, and manually restructure these into my new DB / Entity Framework Models?

1
1
1/27/2018 1:16:57 PM

Accepted Answer

This is the auto-generated class you get from such JSON. The tricky bit here is List of primitive types.

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

Certain databases like PostgreSQL supports array as primitive type. If you are using PostgreSQL then you can perhaps make those properties array of primitive type and store them on server as is.

For other databases which does not support array, you cannot store a list of primitive values into single column of database. The easiest way to deal with it is to introduce serialization and create single string which can be stored to server. So looking at above class, for public List<string> topLevelDomain property, you can rewrite it in following way,

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

With NotMapped attribute EF will not map topLevelDomain property. But TopLevelDomainString will be persisted to database and it will get values from topLevelDomain. As for Serialize/Deserialize methods, you can use any serialization method. You can use JsonSerializer directly (since you are already using JSON objects. Or you can just combine strings using , as delimiter and split string from server using it.

Starting with EF Core 2.1 version, you can use Value-Conversion feature directly to provide funcs to do conversion (essentially serialization code like above) to EF and EF will do it while reading/saving data from/to server. This will avoid you having to create additional CLR property.

2
4/25/2018 10:07:28 PM

Popular Answer

Here is your auto-generated class:

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

Let's prepare another simple one:

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

Now you could first do encoding from json to class RootObject, and then create MyRootObject based on it:

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

Sure MyRootObject is only an example.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow