0

(これは、私の問題に関するコンテキストを提供するためのあらゆる種類の背景です。「問題」までスキップしてそれを読んでから、戻って背景をざっと読みたい場合は、問題を解決してください。申し訳ありません。テキストの壁!)


データベースに保存する必要のある、ひどい、ひどいJSONがたくさんあります。基本的に、誰かが大きなXMLファイルを取得し、XMLのXPathを使用するだけで、それを1つの大きなフラットなJSONオブジェクトにシリアル化しました。これが私が意味することの例です:

元のXML:

<statistics>
    <sample>
        <date>2012-5-10</date>
        <duration>11.2</duration>
    </sample>
    <sample>
        <date>2012-6-10</date>
        <duration>13.1</duration>
    </sample>
    <sample>
        <date>2012-7-10</date>
        <duration>10.0</duration>
    </sample>
</statistics>

私が使用しなければならない恐ろしいJSON:(基本的には上からのXPathだけ)

{
    "statistics":"",

    "statistics/sample":"",
    "statistics/sample/date":"2012-5-10",
    "statistics/sample/duration":"11.2",

    "statistics/sample@1":"",
    "statistics/sample/date@1":"2012-6-10",
    "statistics/sample/duration@1":"13.1",

    "statistics/sample@2":"",
    "statistics/sample/date@2":"2012-7-10",
    "statistics/sample/duration@2":"10.0",
}

statisticsそして今、私はそれをと列を持つテーブルを含むデータベースに置く必要がdateありdurationます。

私が現在どのようにやっているのか:(または少なくともその方法の簡単な例)

Tuple<string, string>[] jsonToColumn = // maps JSON value to SQL table column
{
    new Tuple<string, string>("statistics/sample/date", "date"),
    new Tuple<string, string>("statistics/sample/duration", "duration")
};

// Parse the JSON text
// jsonText is just a string holding the raw JSON
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> json = serializer.DeserializeObject(jsonText) as Dictionary<string, object>;

// Duplicate JSON fields have some "@\d+" string appended to them, so we can
// find these and use them to help uniquely identify each individual sample.
List<string> sampleIndices = new List<string>();
foreach (string k in json.Keys)
{
    Match m = Regex.Match(k, "^statistics/sample(@\\d*)?$");
    if (m.Success)
    {
        sampleIndices .Add(m.Groups[1].Value);
    }
}

// And now take each "index" (of the form "@\d+" (or "" for the first item))
// and generate a SQL query for its sample.
foreach (string index in compressionIndices)
{
    List<string> values = new List<string>();
    List<string> columns = new List<string>();
    foreach (Tuple<string, string> t in jsonToColumn)
    {
        object result;
        if (json.TryGetValue(t.Item1 + index, out result))
        {
            columns.Add(t.Item2);
            values.Add(result);
        }
    }

    string statement = "INSERT INTO statistics(" + string.Join(", ", columns) + ") VALUES(" + string.Join(", ", values) + ");";
    // And execute the statement...
}

ただし、更新を挿入して適用する前にクエリの実行を開始し、独自のSQLステートメントを作成して実行する必要があるため、このハッカーではなくADO.NETエンティティデータモデル(またはLINQ風のもの)を使用したいと思います。ただ...面倒です。ADO.NETエンティティデータモデル(.edmx)ファイルを作成して設定しました。これで、このモデルを使用してデータベースを簡単に操作したり、データベースに書き込んだりできます。


問題/質問

問題は、JSONからADO.NETエンティティデータモデルオブジェクト(テーブルStatistic内のサンプル/レコードを表す)に最適にマップする方法がわからないことです。statistics最も簡単なのは、Tupleメンバーへのポインター(Tuple<string, Statistic::*Duration>("statistics/sample/duration", &Statistic::Duration)これがC ++の場合はala)のようなものを使用するようにリストを変更することですが、a)C#ではこれが可能だとは思わない、b)それができたとしても私はTupleすべて異なるタイプを持っています。

ここでの私のオプションのいくつかは何ですか?StatisticJSONをオブジェクトに最適にマッピングするにはどうすればよいですか?私はLINQの世界に少し慣れていないので、これらの値を(LINQまたは他の何かを介して)マップする方法があるかどうか疑問に思っています。

それは私がいる(そのような貧弱なJSONで作業している)最適ではない立場であり、私の現在の方法が私の状況を考えると他の何よりも優れている可能性があることを認識しています。答え。しかし、このJSONをC#オブジェクト(そして最終的にはSQLデータベース)にマッピングするためのオプションについて調べたいと思います。

4

2 に答える 2

1

ORM(EntityFrameworkなど)を使用している場合は、データモデルに対して作業しているだけなので、モデルクラスが次のように定義されていると仮定します...

public class Sample
{
    public string Date { get; set; }
    public double Duration { get; set; }
}

次に、このようなことを行うことができます...

List<Sample> samples = new List<Sample>();
Dictionary<string, object> data = ser.DeserializeObject(json) as Dictionary<string, object>;
var keys =  data.Keys.ToList();
for (int i = 0; i <keys.Count; i++)
{
    string k = keys[i];
    if (Regex.IsMatch(k, "^statistics/sample(@\\d*)?$"))
    {
        samples.Add(new Sample
        {
            Date = (string)data[keys[i + 1]],
            Duration = double.Parse((string)data[keys[i + 2]])
        });
    }
}

例のリストにデータを入力するだけです。EntityFrameworkのようなものを使用している場合は、インスタンスをリポジトリ/データコンテキストに直接追加することもできます。

于 2012-08-31T00:57:58.303 に答える
1

問題全体がその「JSON」をPOCOエンティティにマッピングすることである場合、カスタムを使用してそれを逆シリアル化する方法の例を次に示しますJavascriptConverter

POCOエンティティ:

public class Statistics
{
    public Statistics()
    {
        Samples = new List<Sample>();
    }

    public List<Sample> Samples { get; set; }
}

public class Sample
{

    public DateTime Date { get; set; }
    public float Duration { get; set; }
}

StatsConverter:

public class StatsConverter : JavaScriptConverter
{
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");
        else if (type == typeof(Statistics))
        {
            Statistics statistics = null;
            Sample sample = null;
            {
                foreach (var item in dictionary.Keys)
                {
                    if (dictionary[item] is string && item.Contains("duration"))
                        sample.Duration = float.Parse(dictionary[item].ToString());
                    else if (dictionary[item] is string && item.Contains("date"))
                        sample.Date = DateTime.Parse((dictionary[item].ToString()));
                    else if (dictionary[item] is string && item.Contains("sample"))
                    {
                        sample = new Sample();
                        statistics.Samples.Add(sample);
                    }
                    else if (dictionary[item] is string && item.Contains("statistics"))
                        statistics = new Statistics();
                }
            }
            return statistics;
        }
        return null;
    }    

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(Statistics)})); }
    }
}

次に、逆シリアル化する方法のサンプルを示します。

string json = @"{
""statistics"":"""",

""statistics/sample"":"""",
""statistics/sample/date"":""2012-5-10"",
""statistics/sample/duration"":""11.2"",

""statistics/sample@1"":"""",
""statistics/sample/date@1"":""2012-6-10"",
""statistics/sample/duration@1"":""13.1"",

""statistics/sample@2"":"""",
""statistics/sample/date@2"":""2012-7-10"",
""statistics/sample/duration@2"":""10.0""

} ";

//These are the only 4 lines you'll require on your code
JavaScriptSerializer serializer = new JavaScriptSerializer();
StatsConverter sc = new StatsConverter();
serializer.RegisterConverters(new JavaScriptConverter[] { new StatsConverter() });
Statistics stats = serializer.Deserialize<Statistics>(json);

stats上記のオブジェクトは、コレクションStatisticsに3つのオブジェクトがあるオブジェクトに逆シリアル化されます。SampleSamples

于 2012-08-31T00:58:40.160 に答える