16

DefaultValue適切な動作のために属性を使用してPropertyGridいます (デフォルトとは異なる値を太字で示しています)。を使用して表示されたオブジェクトをシリアル化する場合XmlSerializer、デフォルト値を持つプロパティの xml-file にエントリはありません。

これらをまだシリアル化するように XmlSerializer に指示する最も簡単な方法は何ですか?

「バージョン」をサポートするにはそれが必要なので、コードの後半でデフォルト値を変更すると、シリアル化されたプロパティは「最新」のものではなく、シリアル化された値を取得します。私は次のことを考えることができます:

  • の動作をオーバーライドしますPropertyGrid(カスタム属性を使用するため、 によって無視されXmlSerializerます);
  • カスタムの xml-serialization の並べ替えを行いDefaultValueます。
  • に渡す前にオブジェクトで何かをして、XmlSeriazerが含まれないようにしDefaultValueます。

しかし、あまり苦労せずにそれを行うことができるいくつかの秘密のプロパティを見逃す可能性があります = D.

これが私が欲しいものの例です:

    private bool _allowNegative = false;
    /// <summary>
    /// Get or set if negative results are allowed
    /// </summary>
    [Category(CategoryAnalyse)]
    [Admin]
    [TypeConverter(typeof(ConverterBoolOnOff))]
    //[DefaultValue(false)] *1
    public bool AllowNegative
    {
        get { return _allowNegative; }
        set
        {
            _allowNegative = value;
            ConfigBase.OnConfigChanged();
        }
    }
    //public void ResetAllowNegative() { _allowNegative = false; } *2
    //public bool ShouldSerializeAllowNegative() { return _allowNegative; } *3
    //public bool ShouldSerializeAllowNegative() { return true; } *4

コメントを外すと (*1)、必要な効果が得PropertyGridられます。デフォルト値を持つプロパティは通常のテキストで表示されます。それ以外の場合、テキストは太字になります。ただし、デフォルト値のプロパティをxmlファイルに入れません。これはXmlSerializer悪いことです(そして私はそれを修正しようとしています)。

(※2)と(※3)のコメントを外すと(※1)のコメントを外すのと全く同じです。

(*2) と (*4) のコメントを外すと、XmlSerializer常にプロパティが xml ファイルに入れられますが、これはデフォルト値がなくなりPropertyGridすべての値が太字で表示されるために発生します。

4

4 に答える 4

7

Xml に属性が必要ない限り、代わりにDataContractSerializerを使用すると、希望する動作が得られます。

[DataContract]
public class Test
{
    [DataMember]
    [DefaultValue(false)]
    public bool AllowNegative { get; set; }
}

void Main()
{
    var sb2 = new StringBuilder();
    var dcs = new DataContractSerializer(typeof(Test));

    using(var writer = XmlWriter.Create(sb2))
    {
        dcs.WriteObject(writer, new Test());
    }

    Console.WriteLine(sb2.ToString());  
}

生成します(名前空間などを除く)

<Test>
    <AllowNegative>false</AllowNegative>
</Test>
于 2013-03-28T13:10:58.367 に答える
3

あなたが探しているのは と だと思いShouldSerialize()ますReset()。これらを使用すると、クラスがもう少し拡張されますが (プロパティごとに 2 つの関数を使用)、探しているものを具体的に実現できます。

簡単な例を次に示します。

// your property variable
private const String MyPropertyDefault = "MyValue";
private String _MyProperty = MyPropertyDefault;

// your property
// [DefaultValueAttribute("MyValue")] - cannot use DefaultValue AND ShouldSerialize()/Reset()
public String MyProperty
{
    get { return _MyProperty; }
    set { _MyProperty = value; }
}


// IMPORTANT!
// notice that the function name is "ShouldSerialize..." followed
// by the exact (!) same name as your property
public Boolean ShouldSerializeMyProperty()
{
    // here you would normally do your own comparison and return true/false
    // based on whether the property should be serialized, however,
    // in your case, you want to always return true when serializing!

    // IMPORTANT CONDITIONAL STATEMENT!
    if (!DesignMode)
        return true; // always return true outside of design mode (is used for serializing only)
    else
        return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value
}

public void ResetMyProperty()
{
    _MyProperty = MyPropertyDefault;
}

PropertyGrid 機能をそのまま維持したいので、関数が呼び出されたときにシリアル化しているかどうかを知る必要があることに注意してください。ShouldSerialize()シリアライズ時に設定されるある種の制御フラグを実装することをお勧めしますreturn true


属性をおよび関数と組み合わせて使用​​することはできないことに注意してください(または のみを使用します)。DefaultValueShouldSerialize()Reset()


編集:関数の説明を追加しShouldSerialize()ます。

現在、既定値をシリアル化し、プロパティに既定値があることを PropertyGrid に知らせる方法がないため、デザイン モードかどうかを確認する条件を実装する必要があります。

Componentクラスがまたはから派生すると仮定すると、設計時にのみControlVisual Studio によって設定されるプロパティがあります。条件は次のようになります。DesignMode

if (!DesignMode)
    return true; // always return true outside of design mode (is used for serializing only)
else
    return _MyProperty != MyPropertyDefault; // during design mode, we actually compare against the default value

編集 2: Visual Studio のデザイン モードについて話しているのではありません。

上記のコードを念頭に置いて、 という別のプロパティを作成しますIsSerializing。を呼び出すIsSerializingにプロパティをに設定し、を呼び出したに設定を解除します。true XmlSerializer.Serialize

最後に、if (!DesignMode)条件ステートメントを に変更しますif (IsSerializing)

于 2013-03-27T17:20:38.727 に答える
1

缶のこの動作は、XmlAttributeOverridesXmlSerializerで上書きでき ます。

ここからアイデアを借りました:

static public XmlAttributeOverrides GetDefaultValuesOverrides(Type type)
{
    XmlAttributeOverrides explicitOverrides = new XmlAttributeOverrides();

    PropertyDescriptorCollection c = TypeDescriptor.GetProperties(type);
    foreach (PropertyDescriptor p in c)
    {
        AttributeCollection attributes = p.Attributes;
        DefaultValueAttribute defaultValue = (DefaultValueAttribute)attributes[typeof(DefaultValueAttribute)];
        XmlIgnoreAttribute noXML = (XmlIgnoreAttribute)attributes[typeof(XmlIgnoreAttribute)];
        XmlAttributeAttribute attribute = (XmlAttributeAttribute)attributes[typeof(XmlAttributeAttribute)];

        if ( defaultValue != null && noXML == null )
        {
            XmlAttributeAttribute xmlAttribute = new XmlAttributeAttribute(attribute.AttributeName);
            XmlAttributes xmlAttributes = new XmlAttributes();
            xmlAttributes.XmlAttribute = xmlAttribute;
            explicitOverrides.Add(userType, attribute.AttributeName, xmlAttributes);
        }
    }
    return explicitOverrides;
}

そして、デフォルト値を発行する必要があるクラスを装飾するために、自分自身を属性にしました。すべてのクラスでこれを行いたい場合は、コンセプト全体を適応させることができると確信しています。

Public Class EmitDefaultValuesAttribute
    Inherits Attribute
    Private Shared mCache As New Dictionary(Of Assembly, XmlAttributeOverrides)

    Public Shared Function GetOverrides(assembly As Assembly) As XmlAttributeOverrides
        If mCache.ContainsKey(assembly) Then Return mCache(assembly)
        Dim xmlOverrides As New XmlAttributeOverrides
        For Each t In assembly.GetTypes()
            If t.GetCustomAttributes(GetType(EmitDefaultValuesAttribute), True).Count > 0 Then
                AddOverride(t, xmlOverrides)
            End If
        Next
        mCache.Add(assembly, xmlOverrides)
        Return xmlOverrides
    End Function

    Private Shared Sub AddOverride(t As Type, xmlOverrides As XmlAttributeOverrides)
        For Each prop In t.GetProperties()
            Dim defaultAttr = prop.GetCustomAttributes(GetType(DefaultValueAttribute), True).FirstOrDefault()
            Dim xmlAttr As XmlAttributeAttribute = prop.GetCustomAttributes(GetType(XmlAttributeAttribute), True).FirstOrDefault()
            If defaultAttr IsNot Nothing AndAlso xmlAttr IsNot Nothing Then
                Dim attrs As New XmlAttributes '= {New XmlAttributeAttribute}
                attrs.XmlAttribute = xmlAttr
                ''overide.Add(t, xmlAttr.AttributeName, attrs)
                xmlOverrides.Add(t, prop.Name, attrs)
            End If
        Next
    End Sub

xsd.exe部分クラスを生成するため、これEmitDefaultValuesAttributeを別のファイルに追加できます。

<EmitDefaultValuesAttribute()>
Public MyClass
    Public Property SubClass() As MySubClass
End Class

<EmitDefaultValuesAttribute()>
Public MySubClass
End Class

使用方法は次のとおりです。

Dim serializer As New XmlSerializer(GetType(MyClass), EmitDefaultValuesAttribute.GetOverrides(GetType(MyClass).Assembly))
于 2014-11-12T18:05:03.010 に答える