2016-12-30 4 views
2

httpエンドポイントから大きなjsonファイルを解析してカスタムローカルクラスインスタンスに値をコピーするのは簡単なC++タスクです。 「シンプル」C++テクニックを使用したコードの簡略化

このコードはある(例えば、以下のコードではセッターsetField1setField2、などとobjである)が、JSONレスポンスは、同じことを非常に大きなC++ファイルに巨大で、結果。しかし、考慮すべき事項がいくつかあります。つまり、タイプとセッターのメソッド名が異なります。以下は3つのケース(int、bool、double)ですが、私のコードには少なくとも50種類あります。コードを近代化し、エラーを起こしにくくし、コードの行数を減らすにはどうすればいいですか?

if (item_[i].HasMember("field1") && item_[i]["field1"].IsDouble()) { 

    double v = item_[i]["field1"].GetDouble(); 
    if (v < 0) 
     throw CustomException("field1 value invalid"); 

    obj.setField1(v); 

} else { 
    throw CustomException("field1 missing or wrong data type"); 
} 

if (item_[i].HasMember("field2") && item_[i]["field2"].IsBool()) { 

    bool v = item_[i]["field2"].GetBool(); 

    obj.setField2(v); 

} else { 
    throw CustomException("field2 missing or wrong data type"); 
} 

if (item_[i].HasMember("field3") && item_[i]["field3"].IsInt()) { 

    int v = item_[i]["field3"].GetInt(); 
    if (v < 0) 
     throw CustomException("field3 value invalid"); 

    obj.setField3(v); 

} else { 
    throw CustomException("field3 missing or wrong data type"); 
} 
+1

これらの 'IsInt'、' GetInt'メソッドは 'Is 'と 'Get 'メソッドとして取得できますか?その後、それはかなり些細なことだろう。 – nvoigt

+0

これは、[コードレビューの要件]を考慮せずに同じ正確な質問をクロス投稿したためです(http://meta.codereview.stackexchange.com/questions/5777/a-guide-to-code-review-for-stack -overflow-users)。スタックオーバーフローは一般的な質問です。コードレビューには実際のコードが必要です。 –

+0

'' item_ [i] ["fieldName"] 'をそのフィールドを持たないアイテムに対して実行するとどうなりますか?もう一歩、 'bool'以外のフィールドで' GetBool() 'を呼び出すとどうなりますか?答えはどちらの場合でも "例外"になると思いますが、コードを一連の 'obj.setFieldN(item_ [i] [" fieldN "]。GetType());'あなたのカスタム例外をスローするための単一の 'try' /' catch'。 – Quentin

答えて

1

このシリアライゼーションコードで最も悪いのは、IMHOです。ここでは、C++疑似コードを2セントポストします(私はセッターを使用しませんが、この考え方は簡単に使用することができます)。確かに、この解決策は、あなたのコードに合っていないかもしれませんが、これはちょうど全体のアイデアの概要です。

まず、ここではいくつかの直列化可能なオブジェクトの宣言です:

class TestObject : public JsonSerializable 
{ 
public: 

    TestObject() 
    { 
     // String field names are localized in a single place 
     // Here we create some sort of mapping from JSON to 
     // actual data. 
     addField("bool", &m_bool); 
     addField("int", &m_int); 
     addField("string", &m_string); 
    } 

private: 

    bool  m_bool 
    int   m_int; 
    std::string m_string; 
}; 

今度はJSONファイルからオブジェクトのロードを処理するJsonSerializableクラスを定義してみましょう:最後に

class JsonSerializable 
{ 
public: 

    // This method iterates all registered fields 
    // and tries to read them from a JSON 
    void load(const Json& json) 
    { 
     for (const auto& kv : m_fields) 
     { 
      kv.second->set(json[kv.first]); 
     } 
    } 

protected: 

    // This method was used in a TestObject constructor 
    template<typename TValue> 
    void addField(const std::string& name, TValue* value) 
    { 
     m_fields[name] = new GenericField(value); 
    } 

private: 

    // A map to store all fields to be loaded from JSON 
    // (can be a list, vector or any other favourite container) 
    std::map<std::string, GenericField*> m_fields; 
}; 

少なくともフィールドパーザーのインターフェースはありません:

// An interface that is exposed to JsonSerializable that hides 
// a type-specific serialization process. 
class Field 
{ 
public: 

    // Contains just one method to set a field from a JSON value. 
    virtual void set(const JsonValue& value) = 0; 
}; 

// Generic type-specific implementation 
template<typename TValue> 
class GenericField : public Field 
{ 
public: 

    // Each field contains a pointer to a field, but here you can 
    // also use pointer to a method or std::function to add setters. 
    GenericField(TValue* value) 
     : m_value(value) 
    { 

    } 

    // And here is an actual serialization code, that extracts a 
    // value from a JSON and writes to a pointed chunk of memory. 
    virtual void set(const JsonValue& value) 
    { 
     *m_value = value.as<TValue>(); 
    } 

private: 

    TValue*   m_value; 
}; 

ここでの基本的な考え方Fieldインタフェースの背後にある実際のシリアライゼーションコードを隠し、シリアライズ可能オブジェクトのコンストラクタ内の単一の場所に文字列識別子をローカライズすることによって、コードの重複を排除することです。

これが役に立ちます。

1

は私が

int err = 0; 
JSONParser jparser(json_as_stdstring); 
x = jparser.getDouble("fielda, &err); 
if(err) 
    /* we have an error */ 

しかしerrがスティッキーで、このインターフェイスを持つJSONパーサーを持っています。だから、コードはまったくJSONのいずれかのエラーが全体の変換を無効にする一般的な状況では、それは構文解析のうちの複雑さを保ち、この

int err = 0; 
JSONParser jparser(json_as_stdstring); 
Myclass myclass; // object to fill; 
myclass.x = jparser.getDouble("fielda", &err); 
myclass.name = jparser.getString("name", &err); 
myclass.id = jparser.getInteger("id" &err); 
if(err) 
    /* we have an error */ 

のように見えることができます。あなたが悪いデータや紛失したデータを許容することができれば、それを処理してエラーを0にリセットします。

0

まず、フィールドの型をチェックし、そのC++型に基づいて値をフェッチする方法が必要です。上記で

template <typename T> 
bool Is(const Field &field) { return false; } 

template <> bool Is<bool>(const Field &field) { return field.IsBool(); } 
template <> bool Is<double>(const Field &field) { return field.IsDouble(); } 
template <> bool Is<int>(const Field &field) { return field.IsInt(); } 

template <typename T> 
T Get(const Field &field) { throw 0; } 

template <> T Get<bool>(const Field &field)  { return field.GetBool(); } 
template <> T Get<double>(const Field &field) { return field.GetDouble(); } 
template <> T Get<int>(const Field &field)  { return field.GetInt(); } 

、私はあなたが種類の数が限られているし、それらのそれぞれのためにこれらの関数テンプレートを専門にすることは大したことではないと仮定しています。 Fieldは、特定のフィールドをリクエストしたときにJSONパーサーが返すタイプです。

これで、一般的な抽出機能を構築できます。私はあなたの例がこのステップで妥当性検査を行っていることに気付きましたが、それを分けるつもりです。 JSONThingは、JSONオブジェクトの型が何であれ(元の投稿のitem_[i]の型と同じです)。

template <typename T> 
T Extract(const JSONThing &json, const char * field_name) { 
    if (!json.HasMember(field_name)) throw CustomException("missing field") 
    const Field &field = json[field_name]; 
    if (!Is<T>(field)) throw CustomException("wrong type"); 
    return Get<T>(field); 
} 

検証の場合、入力値が有効な場合は入力値を返す関数(または関数テンプレート)を使用できます。確かに、これは少し工夫されていますが、オブジェクトをかなり簡単に取り込むことができます。

template <typename T> 
const T &Nonnegative(const T &value) { 
    if (value < static_cast<T>(0)) throw CustomException("invalid value"); 
    return value; 
} 

今、あなたはこのようなあなたのオブジェクトを移入することができます

const auto &json = item_[i]; 
obj.setField1(Nonnegative(Extract<double>(json, "Field1"))); 
obj.setField2(Extract<bool>(json, "Field2")); 
obj.setField3(Nonnegative(Extract<int>(json, "Field3"))); 
// ... 

私はこれはかなり読みやすい見つけ、そしてそれは、事実上すべての重複を排除し、そのミスを犯すために少しのチャンスがあります。カスタム例外の詳細を知りたい場合は、もう少し作業をしなければなりません。