2012-07-12 73 views
5

我的問題很長...所以,請耐心等待:)如何忽略ExtJS數據模型中的空字段?

我正在使用ExtJS 4中的模型,但我在關聯中遇到了一些問題,所以我創建了一個函數來執行自動爲我創建模型。讓我們假設我需要分析以下JSON:

{ 
    "success": true, 
    "total": 28, 
    "itens": [{ 
     "id":1, 
     "nome":"ACRE", 
     "sigla":"AC", 
     "pais":{ 
      "id":31, 
      "nome":"BRASIL", 
      "sigla":"BR" 
     } 
    },{ 
     "id":2, 
     "nome":"ALAGOAS", 
     "sigla":"AL", 
     "pais":{ 
      "id":31, 
      "nome":"BRASIL", 
      "sigla":"BR" 
     } 
    }, ...] 
} 

的itens代表各省(ESTADOS巴西葡萄牙語),其中有一個國家(巴西葡萄牙語國家報)。我試圖使用ExtJS關聯,但我認爲它像Java關係一樣工作,我錯了。那麼,對於這個JSON我有這些Java類和這些Ext模型(模型是使用提供的功能創建的)。

Pais.java

@Entity 
// named queries here... 
public class Pais implements Serializable { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @NotNull 
    @NotEmpty 
    @Length(max = 100) 
    private String nome; 

    @NotNull 
    @NotEmpty 
    @Column(unique = true) 
    @Length(min = 2, max = 4) 
    private String sigla; 

    // getters, setters, equals, hashCode and toString here 

} 

Estado.java

@Entity 
// named queries here... 
public class Estado implements Serializable { 

    @Id 
    @GeneratedValue 
    private Long id; 

    @NotNull 
    @NotEmpty 
    @Length(max = 100) 
    private String nome; 

    @NotNull 
    @NotEmpty 
    @Column(unique = true) 
    @Length(min = 2, max = 4) 
    private String sigla; 

    @NotNull 
    @ManyToOne 
    private Pais pais; 

    // getters, setters, equals, hashCode and toString here 

} 

函數來創建模型

Ext.ns("Uteis"); 

// other utility functions here... 

Uteis.createModel = function(modelData) { 

    var fields = modelData.fields; 
    var processedFields = []; 
    var normalFields = []; 
    var relationFields = []; 

    for (var i in fields) { 

     if (fields[i].type) { 

      switch (fields[i].type) { 

       case "auto": 
       case "string": 
       case "int": 
       case "float": 
       case "boolean": 
       case "date": 
        normalFields.push(fields[i]); 
        break; 

       default: 

        var relationField = fields[i]; 

        var prefix = relationField.name + "."; 
        var modelInstance = Ext.create(relationField.type); 

        modelInstance.fields.each(function(item, index, length) { 

         var newField = {}; 

         // I used this sintax to make possible create only some fields 
         // if I need in the future. 
         newField["name"] = prefix + item.name; 
         newField["type"] = item.type.type; 

         newField["convert"] = item.convert; 
         newField["dateFormat"] = item.dateFormat; 
         newField["defaultValue"] = item.defaultValue; 
         newField["mapping"] = item.mapping; 
         newField["persist"] = item.persist; 
         newField["sortDir"] = item.sortDir; 
         newField["sortType"] = item.sortType; 
         newField["useNull"] = item.useNull; 

         relationFields.push(newField); 

        }); 

        break; 

      } 

     } else { 
      normalFields.push(fields[i]); 
     } 

    } 

    processedFields = normalFields.concat(relationFields); 

    // debugging code 
    /*console.log("*** " + modelData.name); 
    for (var i in processedFields) { 
     console.log(processedFields[i]); 
    }*/ 

    Ext.define(modelData.name, { 
     extend: "Ext.data.Model", 
     fields: processedFields 
    }); 

}; 

使用函數來創建模型

Uteis.createModel({ 
    name: "Modelos.Pais", 
    fields: [ 
     { name: "id", type: "int" }, 
     { name: "nome", type: "string" }, 
     { name: "sigla", type: "string" } 
    ] 
}); 

Uteis.createModel({ 
    name: "Modelos.Estado", 
    fields: [ 
     { name: "id", type: "int" }, 
     { name: "nome", type: "string" }, 
     { name: "sigla", type: "string" }, 
     { name: "pais", type: "Modelos.Pais" } // <= references the model created above 
    ] 
}); 

上面的代碼對應了這一點。我創建了自動創建嵌套數據字段的功能(因爲我說的關聯問題)。

Ext.define("Modelos.Pais", { 
    extend: "Ext.data.Model", 
    fields: [ 
     { name: "id", type: "int" }, 
     { name: "nome", type: "string" }, 
     { name: "sigla", type: "string" } 
    ] 
}); 

Ext.define("Modelos.Estado", { 
    extend: "Ext.data.Model", 
    fields: [ 
     { name: "id", type: "int" }, 
     { name: "nome", type: "string" }, 
     { name: "sigla", type: "string" }, 
     { name: "pais.id", type: "int" }, 
     { name: "pais.nome", type: "string" }, 
     { name: "pais.sigla", type: "string" } 
    ] 
}); 

好的,這些模型(使用我的createModel函數創建)與我的JsonStores一起工作得很好。到目前爲止,Java方面的所有映射關聯都不爲null,因此,我的存儲總是有嵌套的數據進行處理。現在,我必須處理一些可能具有空關聯的實體,並且我的問題開始了。需要處理這種情況的商店不起作用(商店操作中引發了一個例外情況,稱這些字段爲空)。我使用Gson從我的實體創建JSON。它的默認行爲是不對空字段進行序列化,它們在客戶端是未定義的,所以如果我序列化null字段(發送null)會使Ext實現空字段而不嘗試處理它。要做到這一點,我用這個代碼來創建GSON:

Gson gson = new GsonBuilder().serializeNulls().create(); 

好了,現在正在生成與空協會JSON,但內線不斷抱怨。我試圖使用字段映射和defaultValue配置沒有成功。爲了使事情更簡單,我們使用Pais不是@NotNull的Estados和Países(Privinces and Countries)的例子。用空派斯的JSON會是這樣:

{ 
    "success": true, 
    "total": 28, 
    "itens": [{ 
     "id":1, 
     "nome":"ACRE", 
     "sigla":"AC", 
     "pais":null // <= here 
    },{ 
     "id":2, 
     "nome":"ALAGOAS", 
     "sigla":"AL", 
     "pais":{ // this is not null 
      "id":31, 
      "nome":"BRASIL", 
      "sigla":"BR" 
     } 
    }, ...] 
} 

有了這個代碼,該pais.id,pais.nome和pais.sigla領域將不可用,因爲派斯屬性爲null。所以,我的問題是:如何使商店忽略某些字段爲空或未定義時?我已經試圖尋找一個沒有成功的解決方案......非常感謝!

編輯:在服務器端的一些可能的解決方案的思維了整個晚上之後,我實現了最後15分鐘的解決方案,但我definatelly不喜歡它...它遍歷每個物體反射的方法在使用Gson將默認值設置爲null的字段之前,使用「對象樹」。它正在工作,但JSON變得不必要的太大了。遍歷方法:

/** 
* A method to traverse the object tree and set "default" values to null fields. 
* 
* @param target The object to be inspected. 
*/ 
public static void traverseAndSetDefaultValue(Object target) { 

    try { 

     for (Field f : target.getClass().getDeclaredFields()) { 

      // ok to change... 
      f.setAccessible(true); 

      // is null? so create something 
      if (f.get(target) == null) { 

       // new instance of the current field 
       Object newInstance = null; 

       // it needs to traverse to the next level? 
       boolean okToTraverse = false; 

       switch (f.getType().getSimpleName()) { 

        case "Byte": 
        case "Short": 
        case "Integer": 
         newInstance = 0; 
         break; 

        case "Long": 
         newInstance = 0L; 
         break; 

        case "Float": 
         newInstance = 0F; 
         break; 

        case "Double": 
         newInstance = 0D; 
         break; 

        case "Character": 
         newInstance = '\0'; 
         break; 

        case "Boolean": 
         newInstance = Boolean.FALSE; 
         break; 

        case "String": 
         newInstance = ""; 
         break; 

        case "List": 
         newInstance = new ArrayList(); 
         break; 

        case "Set": 
         newInstance = new HashSet(); 
         break; 

        default: 
         // calling the default constructor for no 
         // "default" types 
         newInstance = f.getType().newInstance(); 
         okToTraverse = true; 
         break; 

       } 

       f.set(target, newInstance); 

       if (okToTraverse) { 
        traverseAndSetDefaultValue(newInstance); 
       } 

      } 

     } 

    } catch (IllegalAccessException | InstantiationException exc) { 
     exc.printStackTrace(); 
    } 

} 

我想你對它有什麼看法......謝謝! :再次問好。我放棄! :)我將使用我上面發佈的解決方案。我找到了some patches以改善與模型和網格的關係。我測試了它們,但空字段的問題仍然存在(至少錯誤消失了)。好的,現在是繼續發展的時候了。當應用程序完成後,我會回到這個問題來嘗試改進我的解決方案。謝謝!

+0

我不知道有任何被具體問題使用模型數據中的空值,您可以粘貼'Uteis.createModel'函數的輸出,以便我們可以看到模型定義在運行後會是什麼樣子? 我認爲您需要確保您在模型上添加了與Ext'關聯'屬性的嵌套模型數據,但是我無法看到您的模型中是否存在您的函數。 – dougajmcdonald 2012-07-22 07:32:52

+0

@dougajmcdonald:嗨。我也嘗試過,並在我的帖子中解釋過。當您嘗試在網格列中獲取嵌套值時,這些關聯無法使用null。該函數的輸出在兩個代碼snipets中的代碼下面列出。在第一次我展示了函數調用,第二次展示瞭如果使用Ext.define()並擴展Ext.data.Model時將生成的相應模型。 – davidbuzatto 2012-07-22 15:13:31

+0

我認爲這是您問題的解決方案http://stackoverflow.com/a/12897584/1496088 – 2012-10-15 14:33:38

回答

1

不確定這是什麼問題,因爲你沒有給出確切的錯誤;但我以前曾與可選的嵌套的數據問題,而解決方案是創建模型的映射功能:

Ext.define("Modelos.Estado", { 
    extend: "Ext.data.Model", 
    fields: [ 
     { name: "id", type: "int" }, 
     { name: "nome", type: "string" }, 
     { name: "sigla", type: "string" }, 
     { name: "pais.id", type: "int", mapping: function(o) { return o.pais ? o.pais.id : null; } }, 
     { name: "pais.nome", type: "string", mapping: function(o) { return o.pais ? o.pais.nome : null; } }, 
     { name: "pais.sigla", type: "string", mapping: function(o) { return o.pais ? o.pais.sigla : null; } } 
    ] 
}); 
1

可以擴展Ext.data.reader.Json如下:

Ext.define('Ext.data.reader.SafeJson', { 
    extend: 'Ext.data.reader.Json', 
    alias : 'reader.safejson', 
    /** 
    * @private 
    * Returns an accessor function for the given property string. Gives support for properties such as the following: 
    * 'someProperty' 
    * 'some.property' 
    * 'some["property"]' 
    * This is used by buildExtractors to create optimized extractor functions when casting raw data into model instances. 
    */ 
    createAccessor: (function() { 
     var re = /[\[\.]/; 

     return function(expr) { 
      if (Ext.isEmpty(expr)) { 
       return Ext.emptyFn; 
      } 
      if (Ext.isFunction(expr)) { 
       return expr; 
      } 
      if (this.useSimpleAccessors !== true) { 
       var i = String(expr).search(re); 
       if (i >= 0) { 
        if (i > 0) { // Check all property chain for existence. Return null if any level does not exist. 
         var a = []; 
         var l = expr.split('.'); 
         var r = ''; 
         for (var w in l) { 
          r = r + '.' + l[w]; 
          a.push('obj' + r); 
         } 
         var v = "(" + a.join(" && ") + ") ? obj." + expr + " : null"; 
         return Ext.functionFactory('obj', 'return (' + v + ')'); 
        } else { 
         return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr); 
        } 
       } 
      } 
      return function(obj) { 
       return obj[expr]; 
      }; 
     }; 
    }()), 

     /** 
    * @private 
    * @method 
    * Returns an accessor expression for the passed Field. Gives support for properties such as the following: 
    * 
    * - 'someProperty' 
    * - 'some.property' 
    * - 'some["property"]' 
    * 
    * This is used by buildExtractors to create optimized on extractor function which converts raw data into model instances. 
    */ 
    createFieldAccessExpression: (function() { 
     var re = /[\[\.]/; 

     return function(field, fieldVarName, dataName) { 
      var me  = this, 
       hasMap = (field.mapping !== null), 
       map = hasMap ? field.mapping : field.name, 
       result, 
       operatorSearch; 

      if (typeof map === 'function') { 
       result = fieldVarName + '.mapping(' + dataName + ', this)'; 
      } else if (this.useSimpleAccessors === true || ((operatorSearch = String(map).search(re)) < 0)) { 
       if (!hasMap || isNaN(map)) { 
        // If we don't provide a mapping, we may have a field name that is numeric 
        map = '"' + map + '"'; 
       } 
       result = dataName + "[" + map + "]"; 
      } else {     
       if (operatorSearch > 0) { 
        var a = []; 
        var l = map.split('.'); 
        var r = ''; 
        for (var w in l) { 
         r = r + '.' + l[w]; 
         a.push(dataName + r); 
        } 
        result = "("+a.join(" && ")+") ? "+dataName+"."+map+" : null"; 
       } else { 
        result = dataName + map; 
       } 
      }    
      return result; 
     }; 
    }()) 
}); 

因此,您可以成功處理具有空節點的嵌套JSON數據。 JSON的

例子:

{ 
    root: [{ 
     id: 1, 
     name: { 
      name: "John", 
      phone: "123" 
     },   
    }, 
    { 
     id: 4, 
     name: null,   
    }, 
    ] 
} 

測試數據的工作例如,你可以在這裏找到: http://jsfiddle.net/8Ftag/

ExtJS的4.1.1測試