さまざまなリストを「遅延作成」するいくつかのレガシーオブジェクトをシリアル化しようとしています。従来の動作を変更することはできません。
この簡単な例に要約しました。
public class Junk
{
protected int _id;
[JsonProperty( PropertyName = "Identity" )]
public int ID
{
get
{
return _id;
}
set
{
_id = value;
}
}
protected List<int> _numbers;
public List<int> Numbers
{
get
{
if( null == _numbers )
{
_numbers = new List<int>( );
}
return _numbers;
}
set
{
_numbers = value;
}
}
}
class Program
{
static void Main( string[] args )
{
Junk j = new Junk( ) { ID = 123 };
string newtonSoftJson = JsonConvert.SerializeObject( j, Newtonsoft.Json.Formatting.Indented );
Console.WriteLine( newtonSoftJson );
}
}
現在の結果は次のとおりです。{"ID":123、 "Numbers":[]}
取得したい:{"ID":123}
つまり、空のリスト、コレクション、配列などをスキップします。
これに対する解決策が見つからなかった場合、 答え は、なんとか追跡することができれば非常に簡単です。
元のクラスを拡張することが許可されている場合は、ShouldSerializePropertyName
関数を追加します。これは、クラスの現在のインスタンスに対してそのプロパティをシリアル化する必要があるかどうかを示すブール値を返す必要があります。あなたの例では、これは次のようになります(テストされていませんが、画像を取得する必要があります)。
public bool ShouldSerializeNumbers()
{
return _numbers.Count > 0;
}
このアプローチは私にとってはうまくいきます(VB.NETでも)。元のクラスを変更することを許可されていない場合は、リンクされたページで説明されているIContractResolver
アプローチが最適です。
ブライアンは、インスタンス変数のオーバーヘッドを必要とせず、フィールドとメンバーの両方のインスタンスをトラップする必要があり、コレクション全体を使い果たすために列挙可能なものを必要とするカウント操作を実行する必要はありませんMoveNext()関数。
public class IgnoreEmptyEnumerableResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType != typeof(string) &&
typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
property.ShouldSerialize = instance =>
{
IEnumerable enumerable = null;
// this value could be in a public field or public property
switch (member.MemberType)
{
case MemberTypes.Property:
enumerable = instance
.GetType()
.GetProperty(member.Name)
?.GetValue(instance, null) as IEnumerable;
break;
case MemberTypes.Field:
enumerable = instance
.GetType()
.GetField(member.Name)
.GetValue(instance) as IEnumerable;
break;
}
return enumerable == null ||
enumerable.GetEnumerator().MoveNext();
// if the list is null, we defer the decision to NullValueHandling
};
}
return property;
}
}
IContractResolver
を使用するというDavid Jonesの提案に関して、これは、シリアル化する必要のあるクラスを明示的に変更することなく、すべてのIEnumerables
バリエーションをカバーするのに役立ちます。
public class ShouldSerializeContractResolver : DefaultContractResolver
{
public static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType.GetInterface(nameof(IEnumerable)) != null)
property.ShouldSerialize =
instance => (instance?.GetType().GetProperty(property.PropertyName).GetValue(instance) as IEnumerable<object>)?.Count() > 0;
return property;
}
}
次に、それを設定オブジェクトに組み込みます。
static JsonSerializerSettings JsonSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = ShouldSerializeContractResolver.Instance,
};
次のように使用します:
JsonConvert.SerializeObject(someObject, JsonSettings);
懸命なcommonorgardenになるために、ifテストを次のように構成しました:
public bool ShouldSerializecommunicationmethods()
{
if (communicationmethods != null && communicationmethods.communicationmethod != null && communicationmethods.communicationmethod.Count > 0)
return true;
else
return false;
}
空のリストもしばしばヌルになるので。ソリューションを投稿していただきありがとうございます。 ATB。