从根本上说,我想根据序列化时的值从生成的 Json 中包含或省略属性。
更具体地说,我有一个类型知道是否已为其分配了一个值,并且我只想序列化该类型的属性,如果已经被分配给它(所以我需要在运行时检查值)。我试图让我的 API 能够轻松检测“具有默认值”和“根本未指定”之间的区别。
自定义 JsonConverter 似乎不够;我试过了,我相信属性名称在调用转换器之前已经序列化了。就我而言,我什至想省略属性名称。
我研究过扩展 DefaultContractResolver,但 CreateProperty 和 CreateProperties(返回 JsonProperty 序列化元数据)仅采用被序列化的类型,因此我无法检查实例本身。通常,我在 DefaultContractResolver 上看不到任何允许我控制 if 实例被序列化的东西;也许我错过了。
我还想也许我需要创建一个 ContractResolver 来为我的类型返回一个自定义的 JsonObjectContract。但是,同样,我在 JsonObjectContract 上看不到任何基于实例做出决策的内容。
是否有实现我的目标的好方法?我只是缺少一些简单的东西吗?非常感谢您提供的任何帮助。由于 Json.NET 如此可扩展,我认为这不会太难。但我开始认为我在这里的杂草丛生。 :)
最佳答案
好的,在 Json.NET 源代码中挖掘了一段时间后,我终于得到了这个工作,它甚至会尊重 Json.NET 支持的 ShouldSerialize* 和 *Specified 成员。请注意:这肯定会变成杂草。
所以我意识到 DefaultContractResolver.CreateProperty 返回的 JsonProperty 类具有 ShouldSerialize 和 Converter 属性,它们允许我指定如果属性实例实际上应该被序列化,如果是这样,如何 去做。
不过,反序列化需要一些不同的东西。默认情况下,对于自定义类型,DefaultContractResolver.ResolveContract 将返回具有空 Converter 属性的 JsonObjectContract。为了正确反序列化我的类型,我需要在契约(Contract)适用于我的类型时设置 Converter 属性。
这是代码(删除了错误处理/等以保持尽可能小)。
首先是需要特殊处理的类型:
public struct Optional<T>
{
public readonly bool ValueProvided;
public readonly T Value;
private Optional( T value )
{
this.ValueProvided = true;
this.Value = value;
}
public static implicit operator Optional<T>( T value )
{
return new Optional<T>( value );
}
}
在我们知道它应该被序列化之后,转换器会正确地序列化它:
public class OptionalJsonConverter<T> : JsonConverter
{
public static OptionalJsonConverter<T> Instance = new OptionalJsonConverter<T>();
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer )
{
var optional = (Optional<T>)value; // Cast so we can access the Optional<T> members
serializer.Serialize( writer, optional.Value );
}
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer )
{
var valueType = objectType.GetGenericArguments()[ 0 ];
var innerValue = (T)serializer.Deserialize( reader, valueType );
return (Optional<T>)innerValue; // Explicitly invoke the conversion from T to Optional<T>
}
public override bool CanConvert( Type objectType )
{
return objectType == typeof( Optional<T> );
}
}
最后,也是最详细的,这是插入钩子(Hook)的 ContractResolver:
public class CustomContractResolver : DefaultContractResolver
{
// For deserialization. Detect when the type is being deserialized and set the converter for it.
public override JsonContract ResolveContract( Type type )
{
var contract = base.ResolveContract( type );
if( contract.Converter == null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof( Optional<> ) )
{
// This may look fancy but it's just calling GetOptionalJsonConverter<T> with the correct T
var optionalValueType = type.GetGenericArguments()[ 0 ];
var genericMethod = this.GetAndMakeGenericMethod( "GetOptionalJsonConverter", optionalValueType );
var converter = (JsonConverter)genericMethod.Invoke( null, null );
// Set the converter for the type
contract.Converter = converter;
}
return contract;
}
public static OptionalJsonConverter<T> GetOptionalJsonConverter<T>()
{
return OptionalJsonConverter<T>.Instance;
}
// For serialization. Detect when we're creating a JsonProperty for an Optional<T> member and modify it accordingly.
protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization )
{
var jsonProperty = base.CreateProperty( member, memberSerialization );
var type = jsonProperty.PropertyType;
if( type.IsGenericType && type.GetGenericTypeDefinition() == typeof( Optional<> ) )
{
// This may look fancy but it's just calling SetJsonPropertyValuesForOptionalMember<T> with the correct T
var optionalValueType = type.GetGenericArguments()[ 0 ];
var genericMethod = this.GetAndMakeGenericMethod( "SetJsonPropertyValuesForOptionalMember", optionalValueType );
genericMethod.Invoke( null, new object[]{ member.Name, jsonProperty } );
}
return jsonProperty;
}
public static void SetJsonPropertyValuesForOptionalMember<T>( string memberName, JsonProperty jsonProperty )
{
if( jsonProperty.ShouldSerialize == null ) // Honor ShouldSerialize*
{
jsonProperty.ShouldSerialize =
( declaringObject ) =>
{
if( jsonProperty.GetIsSpecified != null && jsonProperty.GetIsSpecified( declaringObject ) ) // Honor *Specified
{
return true;
}
object optionalValue;
if( !TryGetPropertyValue( declaringObject, memberName, out optionalValue ) &&
!TryGetFieldValue( declaringObject, memberName, out optionalValue ) )
{
throw new InvalidOperationException( "Better error message here" );
}
return ( (Optional<T>)optionalValue ).ValueProvided;
};
}
if( jsonProperty.Converter == null )
{
jsonProperty.Converter = CustomContractResolver.GetOptionalJsonConverter<T>();
}
}
// Utility methods used in this class
private MethodInfo GetAndMakeGenericMethod( string methodName, params Type[] typeArguments )
{
var method = this.GetType().GetMethod( methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static );
return method.MakeGenericMethod( typeArguments );
}
private static bool TryGetPropertyValue( object declaringObject, string propertyName, out object value )
{
var propertyInfo = declaringObject.GetType().GetProperty( propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance );
if( propertyInfo == null )
{
value = null;
return false;
}
value = propertyInfo.GetValue( declaringObject, BindingFlags.GetProperty, null, null, null );
return true;
}
private static bool TryGetFieldValue( object declaringObject, string fieldName, out object value )
{
var fieldInfo = declaringObject.GetType().GetField( fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance );
if( fieldInfo == null )
{
value = null;
return false;
}
value = fieldInfo.GetValue( declaringObject );
return true;
}
}
希望对其他人有所帮助。如果有任何不清楚的地方或我似乎遗漏了什么,请随时提出问题。
关于c# - 根据属性的运行时值选择性地序列化属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12522000/
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最