Json.Net:Html Helper Method 未重新生成 [英] Json.Net: Html Helper Method not regenerating

查看:17
本文介绍了Json.Net:Html Helper Method 未重新生成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个问题,我创建的 ASP.NET MVC html 帮助器方法在每次调用时都没有重新生成".

I'm running into a problem where an ASP.NET MVC html helper method I created is not being "regenerated" each time it is called.

helper 方法的目的是创建要在 angularjs 框架中使用的 Javascript 对象.例如,下面是一个代码片段,其中使用了 helper 方法(从 html 页面的脚本标记中调用):

The purpose of the helper method is to create Javascript objects to be used in an angularjs framework. For example, here's a code snippet where the helper method is used (called from within a script tag of an html page):

var app = angular.module( "appName", ["ui.bootstrap"] );

app.controller( 'appCtrl', function( $scope ) {
    $scope.model = @Html.ToJavascript( Model, new string[] { "FirstName", "LastName", "ID", "Role" } );
} );

Model 是一个具有多种属性的类的实例,但我只想将 FirstName、LastName、ID 和 Role 序列化为 javascript 对象.

Model is an instance of a class which has a variety of properties, but I only want FirstName, LastName, ID and Role to get serialized to a javascript object.

ToJavascript() 辅助方法在 statis 类中定义如下:

The ToJavascript() helper method is defined in a statis class as follows:

   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() );
        }
    }

这利用 Json.NET 进行实际的序列化.

This utilizes Json.NET to do the actual serialization.

Json.NET 的许多很酷的特性之一是它允许您动态定义哪些字段被序列化.这就是 SpecificFieldsResolver 所做的.我定义如下:

One of the many cool features of Json.NET is that it lets you define, on the fly, which fields get serialized. That's what the SpecificFieldsResolver does. I've defined it as follows:

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;
    }
}

令我困惑的是 CreateProperty() 的调用方式.具体来说,它似乎只为被序列化的每种类型的对象调用一次.

What's confusing me is the way that CreateProperty() gets called. Specifically, it seems to only get called once for each type of object being serialized.

这是一个问题,因为在另一个 cshtml 文件中,我对 ToJavascript() 进行了另一个调用,它试图序列化相同类型的对象,但序列化输出的字段不同:

That's a problem because in another cshtml file I have another call to ToJavascript() which is attempting to serialize the same type of object, but with different fields to be output from the serialization:

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" } );
} );

Category 和 VoterID 也是有效的类字段.但是 ToJavascript() 不会对它们进行序列化.相反,它只序列化在第一次调用 ToJavascript() 中定义的字段......即使该调用发生在不同的 cshtml 文件中.就好像 SpecificFieldsResolver 记住了它创建的 JsonProperty 对象.

Category and VoterID are also valid class fields. But ToJavascript() doesn't seralize them. Instead, it only serializes the fields defined in the first call to ToJavascript()...even though that call takes place in a different cshtml file. It's as if SpecificFieldsResolver remembers the JsonProperty objects it creates.

想法?

更新

感谢 dbc 准确诊断问题所在并提出解决方法.我稍微调整了一下,因为我在几个解析器中依赖 Json.NET 的驼峰式名称解析:

Thanx to dbc for diagnosing exactly what was wrong and suggesting a workaround. I adapated it slightly because I rely on Json.NET's camel case name resolution in several resolvers:

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 );
    }

}

现在每个解析器,例如我的 SpecificFieldsResolver,它派生自 MaoDefaultContractResolver 自动继承驼峰式大小写,但避免了 dbc 识别的缓存问题.

Now every resolver, such as my SpecificFieldsResolver, which derives from MaoDefaultContractResolver automatically inherits camel casing but avoids the caching problem the dbc identified.

推荐答案

这似乎是 CamelCasePropertyNamesContractResolver.它的基类 DefaultContractResolver 具有两个构造函数:一个 无参数构造函数和一个 DefaultContractResolver (Boolean) 版本(刚刚在 Json 中过时.NET 7.0).该参数含义如下:

This appears to be a bug with CamelCasePropertyNamesContractResolver. Its base class, DefaultContractResolver, has two constructors: a parameterless constructor, and a DefaultContractResolver (Boolean) version (just made obsolete in Json.NET 7.0). This parameter has the following meaning:

共享缓存

  • 类型:System.Boolean

  • Type: System.Boolean

如果设置为 true,DefaultContractResolver将使用与其他相同类型的解析器共享的缓存.共享缓存将显着提高多个解析器实例的性能,因为昂贵的反射只会发生一次.如果假定解析器的不同实例产生不同的结果,则此设置可能会导致意外行为.当设置为 false 时,强烈建议使用 JsonSerializer 重用 DefaultContractResolver 实例.

If set to true the DefaultContractResolverwill use a cached shared with other resolvers of the same type. Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly recommended to reuse DefaultContractResolver instances with the JsonSerializer.

默认为 false.

不幸的是,默认构造函数 for CamelCasePropertyNamesContractResolver 将值设置为 true:

Unfortunately, the default constructor for CamelCasePropertyNamesContractResolver sets the value to 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
        };
    }
}

此外,没有带有 shareCache 选项的第二个构造函数.这会破坏您的 SpecificFieldsResolver.

Further, there is no second constructor with the shareCache option. This breaks your SpecificFieldsResolver.

作为一种解决方法,您可以从 DefaultContractResolver 派生解析器并使用 CamelCaseNamingStrategy 做名称映射:

As a workaround, you could derive your resolver from DefaultContractResolver and use CamelCaseNamingStrategy to do the name mapping:

public class IndependentCamelCasePropertyNamesContractResolver : DefaultContractResolver
{
    public IndependentCamelCasePropertyNamesContractResolver()
        : base()
    {
        NamingStrategy = new CamelCaseNamingStrategy
        {
            ProcessDictionaryKeys = true,
            OverrideSpecifiedNames = true
        };
    }    
}

public class SpecificFieldsResolver : IndependentCamelCasePropertyNamesContractResolver
{
    // Remainder unchanged
}

请注意,如果您使用的是 9.0 之前的 Json.NET 版本,则 CamelCaseNamingStrategy 不存在.相反,可以使用嵌套的 CamelCasePropertyNamesContractResolver 来映射名称:

Note that if you are using a version of Json.NET prior to 9.0, CamelCaseNamingStrategy does not exist. Instead a kludge nested CamelCasePropertyNamesContractResolver can be used to map the names:

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);
    }
}

这篇关于Json.Net:Html Helper Method 未重新生成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆