如何忽略 ExtJS 数据模型中的空字段?
Posted
技术标签:
【中文标题】如何忽略 ExtJS 数据模型中的空字段?【英文标题】:How to ignore null fields in ExtJS data models? 【发布时间】:2012-07-12 20:38:49 【问题描述】:我的问题很长...所以,请耐心等待:)
我正在使用 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 代表拥有国家(巴西葡萄牙语中的 País)的省(巴西葡萄牙语中的 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 端的所有映射关联都不是空的,所以,我的商店总是有嵌套数据要处理。现在,我必须处理一些可以具有空关联的实体,我的问题开始了。需要处理这种情况的存储不起作用(存储操作中抛出异常,说字段为空)。我正在使用 Gson 从我的实体创建 JSON。它的默认行为是不序列化空字段,它们将在客户端未定义,所以我认为如果我序列化空字段(发送空值)将使 Ext 实现空字段而不尝试处理它。为此,我使用此代码创建了 Gson:
Gson gson = new GsonBuilder().serializeNulls().create();
好的,现在正在生成带有空关联的 JSON,但 Ext 继续抱怨。我尝试使用字段映射和 defaultValue 配置但没有成功。为了使事情更简单,让我们使用 Estados 和 Países(Privinces 和 Country)的示例,其中 Pais 不再是 @NotNull。带有 null pais 的 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 字段将不可用,因为 pais 属性为空。所以,我的问题是:当某些字段为空或未定义时,如何让商店忽略它们?我已经尝试寻找没有成功的解决方案...非常感谢!
编辑:在服务器端考虑了整个晚上的一些可能的解决方案后,我在最后 15 分钟内实现了一个解决方案,但我绝对不喜欢它......它是一种反思方法在使用 Gson 将默认值设置为空字段之前遍历每个对象“对象树”。它正在工作,但是 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();
我想知道您对此有何看法...谢谢!
编辑 2:您好。我放弃! :) 我将使用我在上面发布的解决方案。我找到了some patches 来改善与模型和网格的关系。我测试了它们,但空字段的问题仍然存在(至少错误消失了)。好的,现在是继续开发的时候了。应用程序完成后,我将回到这个问题以尝试改进我的解决方案。谢谢!
【问题讨论】:
我不知道模型数据中的空值存在任何具体问题,您能否粘贴您的“Uteis.createModel”函数的输出,以便我们查看模型定义之后的样子它运行了吗?我认为您需要确保在模型上添加带有 Ext 'associations' 属性的嵌套模型数据,但我看不到用于制作模型的函数中是否/在哪里。 @dougajmcdonald:嗨。我也试过了,我在帖子里解释了。当您尝试在网格列中获取嵌套值时,关联不适用于 null。该函数的输出在两个代码片段中的代码下方列出。在第一个中,我展示了函数调用,在第二个中,如果我使用 Ext.define() 并扩展 Ext.data.Model,将生成相应的模型。 我认为这是您的问题的解决方案***.com/a/12897584/1496088 【参考方案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;
]
);
【讨论】:
【参考方案2】:你可以扩展 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 测试
【讨论】:
以上是关于如何忽略 ExtJS 数据模型中的空字段?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用父“模型”访问子“模型”数据以在 extjs 的网格中显示
IntegrityError:postgres 从转储恢复后,所有具有 ForeignKey 的模型/字段的“id”列中的空值