Json.Net:Html Helper 方法没有重新生成 [英] Json.Net: Html Helper Method not regenerating

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

问题描述

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

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 自动继承camel 大小写,但避免了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.

不幸的是,默认构造函数 对于 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 方法没有重新生成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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