Mar*_*ert 2 asp.net-mvc json.net angularjs
我遇到了一个问题,我创建的ASP.NET MVC html帮助器方法每次调用时都不会被"重新生成".
辅助方法的目的是创建要在angularjs框架中使用的Javascript对象.例如,这是一个代码片段,其中使用了helper方法(从html页面的脚本标记内调用):
var app = angular.module( "appName", ["ui.bootstrap"] );
app.controller( 'appCtrl', function( $scope ) {
$scope.model = @Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role" } );
} );
Run Code Online (Sandbox Code Playgroud)
Model是一个具有各种属性的类的实例,但我只希望将FirstName,LastName,ID和Role序列化为javascript对象.
ToJavascript()帮助器方法在statis类中定义如下:
public static HtmlString ToJavascript( this HtmlHelper helper, object toConvert, string[] includedFields = null, Formatting formatting = Formatting.Indented, ReferenceLoopHandling loopHandling = ReferenceLoopHandling.Ignore )
{
using( var stringWriter = new StringWriter() )
using( var jsonWriter = new JsonTextWriter( stringWriter ) )
{
var serializer = new JsonSerializer()
{
// Let's use camelCasing as is common practice in JavaScript
ContractResolver = new SpecificFieldsResolver( includedFields ),
Formatting = formatting,
ReferenceLoopHandling = loopHandling,
};
// We don't want quotes around object names
jsonWriter.QuoteName = false;
serializer.Serialize( jsonWriter, toConvert );
return new HtmlString( stringWriter.ToString() );
}
}
Run Code Online (Sandbox Code Playgroud)
这利用Json.NET进行实际的序列化.
Json.NET的许多很酷的功能之一是它允许您在运行中定义哪些字段被序列化.这就是SpecificFieldsResolver所做的.我把它定义如下:
public class SpecificFieldsResolver : CamelCasePropertyNamesContractResolver
{
private string[] _included;
public SpecificFieldsResolver( string[] included )
{
_included = included;
}
protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization )
{
JsonProperty prop = base.CreateProperty( member, memberSerialization );
bool inclField = ( _included == null )
|| _included.Contains( member.Name, StringComparer.CurrentCultureIgnoreCase );
prop.ShouldSerialize = obj => inclField;
return prop;
}
}
Run Code Online (Sandbox Code Playgroud)
令我困惑的是CreateProperty()被调用的方式.具体来说,似乎只对被序列化的每种类型的对象调用一次.
这是一个问题,因为在另一个cshtml文件中我再次调用ToJavascript(),它试图序列化相同类型的对象,但是要从序列化输出不同的字段:
var app = angular.module( "app2Name", ["ui.bootstrap"] );
app.controller( 'app2Ctrl', function( $scope ) {
$scope.model = @Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role", "Category", "VoterID" } );
} );
Run Code Online (Sandbox Code Playgroud)
Category和VoterID也是有效的类字段.但是ToJavascript()并没有对它们进行分类.相反,它只序列化第一次调用ToJavascript()中定义的字段...即使该调用发生在不同的cshtml文件中.就好像SpecificFieldsResolver会记住它创建的JsonProperty对象一样.
思考?
更新
Thanx到dbc用于准确诊断出错误并建议解决方法.我稍微调整了一下因为我在几个解析器中依赖于Json.NET的驼峰案例名称解析:
public class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
public string ToCamelCase( string propertyName )
{
return ResolvePropertyName( propertyName );
}
}
public class MaoDefaultContractResolver : DefaultContractResolver
{
private CamelCaseNameMapper _mapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName( string propertyName )
{
return _mapper.ToCamelCase( propertyName );
}
}
Run Code Online (Sandbox Code Playgroud)
现在每个解析器,例如我的SpecificFieldsResolver,派生自MaoDefaultContractResolver,都会自动继承驼峰套管,但避免了dbc识别出的缓存问题.
这似乎是一个错误CamelCasePropertyNamesContractResolver.它的基类DefaultContractResolver有两个构造函数:一个无参数构造函数和一个DefaultContractResolver (Boolean)版本(刚刚在Json.NET 7.0中过时).该参数具有以下含义:
shareCache
键入:System.Boolean
如果设置为true,
DefaultContractResolver则将使用与其他相同类型的解析器共享的缓存.共享缓存将显着提高多个解析器实例的性能,因为昂贵的反射只会发生一次.如果假设解析器的不同实例产生不同的结果,则此设置可能会导致意外行为.设置为false时,强烈建议重用DefaultContractResolver实例JsonSerializer.
默认是false.
不幸的是,默认的构造函数用于CamelCasePropertyNamesContractResolver将值设置为true:
public class CamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public CamelCasePropertyNamesContractResolver()
#pragma warning disable 612,618
: base(true)
#pragma warning restore 612,618
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
Run Code Online (Sandbox Code Playgroud)
此外,没有带shareCache选项的第二个构造函数.这会破坏你的SpecificFieldsResolver.
作为一种变通方法,您可以从中获取解析器DefaultContractResolver并用于CamelCaseNamingStrategy执行名称映射:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
public IndependentCamelCasePropertyNamesContractResolver()
: base()
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
};
}
}
public class SpecificFieldsResolver : IndependentCamelCasePropertyNamesContractResolver
{
// Remainder unchanged
}
Run Code Online (Sandbox Code Playgroud)
请注意,如果您使用的是9.0之前的Json.NET版本,CamelCaseNamingStrategy则不存在.相反,嵌套的kludge CamelCasePropertyNamesContractResolver可用于映射名称:
public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
class CamelCaseNameMapper : CamelCasePropertyNamesContractResolver
{
// Purely to make the protected method public.
public string ToCamelCase(string propertyName)
{
return ResolvePropertyName(propertyName);
}
}
readonly CamelCaseNameMapper nameMapper = new CamelCaseNameMapper();
protected override string ResolvePropertyName(string propertyName)
{
return nameMapper.ToCamelCase(propertyName);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
488 次 |
| 最近记录: |