ResXDataNode Class

Represents a resource or metadata element in an XML resource (.resx) file.

Definition

Namespace: KGySoft.Resources
Assembly: KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 9.0.0-preview.1
C#
[SerializableAttribute]
public sealed class ResXDataNode : ISerializable, 
	ICloneable
Inheritance
Object    ResXDataNode
Implements
ICloneable, ISerializable

Remarks

The ResXDataNode class supports the representation of rich data types within a resource file. It can support the storage of any object in a resource file.

You can create a ResXDataNode object by calling one of its overloaded class constructors. You can then add the resource item or element to a resource file by one of the following options:

  Note

If you call any of the SetObject methods of the list above by any Object, then a ResXDataNode instance will be implicitly created. A ResXDataNode instance should be explicitly created only if you want to set the Comment property.

To retrieve a ResXDataNode object from a resource you have the following options:

Example

The following example shows how to retrieve ResXDataNode instances from the IDictionaryEnumerator returned by ResXResourceReader.GetEnumerator and ResXResourceReader.GetMetadataEnumerator methods. Before the actual deserialization you can check the type information if the .resx file is from an untrusted source.
C#
using System;
using System.Collections;
using System.IO;

using KGySoft.Resources;

public class Example
{
    private const string resx = @"<?xml version='1.0' encoding='utf-8'?>
<root>
  <data name='string'>
    <value>Test string</value>
    <comment>Default data type is string.</comment>
  </data>

  <metadata name='meta string'>
    <value>Meta String</value>
  </metadata>

  <data name='int' type='System.Int32'>
    <value>42</value>
  </data>

  <assembly alias='CustomAlias' name='System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' />

  <data name='color' type='System.Drawing.Color, CustomAlias'>
    <value>Red</value>
    <comment>When this entry is deserialized in an unsafe way, System.Drawing assembly will be loaded.</comment>
  </data>

  <data name='bytes' type='System.Byte[]'>
    <value>VGVzdCBieXRlcw==</value>
  </data>

  <data name='dangerous' mimetype='application/x-microsoft.net.object.binary.base64'>
    <value>YmluYXJ5</value>
    <comment>BinaryFormatter will throw an exception for this invalid content.</comment>
  </data>

</root>";

    public static void Main()
    {
        // In SafeMode the enumerator values will be ResXDataNode instances instead of deserialized objects
        var reader = new ResXResourceReader(new StringReader(resx)) { SafeMode = true };

        Console.WriteLine("____Resources in .resx:____");
        Dump(reader.GetEnumerator());

        Console.WriteLine("____Metadata in .resx:____");
        Dump(reader.GetMetadataEnumerator());
    }

    private static void Dump(IDictionaryEnumerator enumerator)
    {
        while (enumerator.MoveNext())
        {
            var node = (ResXDataNode)enumerator.Value;
            Console.WriteLine($"Name: {node.Name}");
            Console.WriteLine($"  Type:        {node.TypeName}");
            Console.WriteLine($"  Alias value: {node.AssemblyAliasValue}");
            Console.WriteLine($"  MIME type:   {node.MimeType}");
            Console.WriteLine($"  Comment:     {node.Comment}");
            Console.WriteLine($"  Raw value:   {node.ValueData}");
            try
            {
                var value = node.GetValueSafe();
                Console.WriteLine($"  Real value:  {value} ({value.GetType()})");
            }
            catch (Exception e)
            {
                Console.WriteLine($"  Safe deserialization of the node threw an exception: {e.Message}");
                try
                {
                    var value = node.GetValue();
                    Console.WriteLine($"  Real value (unsafe):  {value} ({value.GetType()})");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"  Unsafe deserialization of the node threw an exception: {ex.Message}");
                }
            }
            Console.WriteLine();
        }
    }
}

// The example displays the following output:
//  ____Resources in .resx:____
// Name: string
//   Type:
//   Alias value:
//   MIME type:
//   Comment:     Default data type is string.
//   Raw value:   Test string
//   Real value:  Test string (System.String)
// 
// Name: int
//   Type:        System.Int32
//   Alias value:
//   MIME type:
//   Comment:
//   Raw value:   42
//   Real value:  42 (System.Int32)
// 
// Name: color
//   Type:        System.Drawing.Color, CustomAlias
//   Alias value: System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
//   MIME type:
//   Comment:     When this entry is deserialized in an unsafe way, System.Drawing assembly will be loaded.
//   Raw value:   Red
//   Safe deserialization of the node threw an exception: Type "System.Drawing.Color, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" in the data at line 18, position 4 cannot be resolved.
// You may try to specify the expected type or use the unsafe GetValue if the resource is from a trusted source.
//   Real value (unsafe):  Color[Red] (System.Drawing.Color)
// 
// Name: bytes
//  Type:        System.Byte[]
//  Alias value:
//   MIME type:
//   Comment:
//   Raw value:   VGVzdCBieXRlcw==
//   Real value:  System.Byte[] (System.Byte[])
// 
// Name: dangerous
//   Type:
//   Alias value:
//   MIME type:   application/x-microsoft.net.object.binary.base64
//   Comment:     BinaryFormatter will throw an exception for this invalid content.
//   Raw value:   YmluYXJ5
//   Safe deserialization of the node threw an exception: In safe mode it is not allowed to deserialize resource "dangerous" because it was serialized by BinaryFormatter. Line 27, position 4.
//   Unsafe deserialization of the node threw an exception: End of Stream encountered before parsing was completed.
// 
// ____Metadata in .resx:____
// Name: meta string
//   Type:
//   Alias value:
//   MIME type:
//   Comment:
//   Raw value:   Meta String
//   Real value:  Meta String (System.String)

Comparison with System.Resources.ResXDataNode 

If instantiated from a System.Resources.ResXDataNode or System.Resources.ResXFileRef instance, an internal conversion into KGySoft.Resources.ResXDataNode and KGySoft.Resources.ResXFileRef automatically occurs.

  Note

The compatibility with System.Resources.ResXDataNode is provided without any reference to System.Windows.Forms.dll, where that type is located.

Unlike System.Resources.ResXDataNode, this ResXDataNode implementation really preserves the original information stored in the .resx file. No deserialization, assembly loading and type resolving occurs until a deserialization is explicitly requested by calling the GetValue or GetValueSafe methods.

Incompatibility with System.Resources.ResXDataNode:

New features and improvements compared to System.Resources.ResXDataNode:

  • Preserving original type information – The originally stored type information, MIME type and the current assembly alias are preserved (see TypeName, MimeType and AssemblyAliasValue properties). The system version may replace type information with assembly qualified names when the .resx file is parsed. If the assembly qualified name is really needed, you can get it after explicit deserialization by calling GetType().AssemblyQualifiedName on the Object returned by the GetValue method.
  • Raw content – You can use the ValueData property to read the original raw String content stored in the .resx file for this element.
  • Advanced string representation – The ToString method displays the string representation (either of the deserialized object if already cached, or the raw content) so can be used easily in a format argument and provides more debugging information.
  • Security – No deserialization, assembly loading and type resolving occurs until a deserialization is explicitly requested by calling the GetValue method. Additionally, the GetValueSafe methods ensure that only the explicitly specified type or natively supported types are accepted and no assemblies are loaded even if a type is specified by its assembly qualified name. In safe mode resources serialized by BinaryFormatter are completely disabled. You can check the TypeName, MimeType and AssemblyAliasValue properties to get information about the type before obtaining the object. You can even check the raw string content by the ValueData property.
  • Performance – As there is no deserialization and assembly/type resolving during parsing a .resx file by the ResXResourceReader class, the parsing is much faster. This is true even if ResXResourceReader.SafeMode is , because there are always ResXDataNode instances stored internally and deserialization occurs only when a resource is actually accessed.
  • Support of non-serializable types – When serializing an object, the System.Resources.ResXDataNode type throws an InvalidOperationException for non-serializable types. This implementation can serialize also such types (though in safe mode they must be explicitly specified as expected types). It is highly recommended to save such resources with compatible mode disabled (see ResXResourceWriter.CompatibleFormat property and the ResXResourceSet.Save methods) so the BinarySerializationFormatter class can be used for the serialization. If the resource is saved in compatible format, then the legacy BinaryFormatter will used (not supported in .NET 8 and later and deserialization will be denied in safe mode) with an CustomSerializerSurrogateSelector instance to allow the serialization. Though the serialization stream will be compatible with .NET Framework the resource itself might not be able to be deserialized by the System.Resources.ResXResourceReader class. In order to serialize any object in .NET 8 or later, compatible mode must be disabled so the much safer BinarySerializationFormatter class can be used.
  • Support of generics – This ResXDataNode class uses a special SerializationBinder implementation, which supports generic types correctly.

Constructors

ResXDataNode(String, Object) Initializes a new instance of the ResXDataNode class.
ResXDataNode(String, ResXFileRef, String) Initializes a new instance of the ResXDataNode class with a reference to a resource file.

Properties

AssemblyAliasValue Gets the assembly name defined in the source .resx file if TypeName contains an assembly alias name, or , if TypeName contains the assembly qualified name. If the resource does not contain the .resx information (that is, if the ResXDataNode was created from an object or the raw .resx data was removed on a GetValue call), then this property returns .
Comment Gets or sets an arbitrary comment regarding this resource.
FileRef Gets the file reference for this resource, or , if this resource does not have a file reference.
MimeType Gets the MIME type as it is stored in the .resx file for this resource, or , if the mimetype attribute was not defined in the .resx file. If the resource does not contain the .resx information (that is, if the ResXDataNode was created from an object or the raw .resx data was removed on a GetValue call), then this property returns .
Name Gets the name of this resource.
TypeName Gets the type information as String as it is stored in the source .resx file. It can be either an assembly qualified name, or a type name with or without an assembly alias name. If AssemblyAliasValue is not , this property value contains an assembly alias name. The property returns , if the type attribute is not defined in the .resx file. If the resource does not contain the .resx information (that is, if the ResXDataNode was created from an object or the raw .resx data was removed on a GetValue call), then this property returns .
ValueData Gets the raw value data as String as it was stored in the source .resx file. If the resource does not contain the .resx information (that is, if the ResXDataNode was created from an object or the raw .resx data was removed on a GetValue call), then this property returns .

Methods

Clone Creates a new ResXDataNode that is a copy of the current instance.
GetNodeColumnPosition Retrieves the column position of the resource in the resource file.
GetNodeLinePosition Retrieves the line position of the resource in the resource file.
GetValue Retrieves the object that is stored by this node.
GetValueSafe(Boolean) Retrieves the object that is stored by this node, not allowing loading assemblies during the possible deserialization. If the resource is not a natively supported type, then you should use the other overloads.
GetValueSafe(Type, Boolean) Retrieves the object that is stored by this node, specifying the expected type of the result.
GetValueSafe(ITypeResolutionService, String, Boolean) Retrieves the object that is stored by this node, not allowing loading assemblies during the possible deserialization.
See the Remarks section of the other GetValueSafe overloads for details.
Obsolete.
GetValueSafeT(Boolean) Retrieves the object that is stored by this node, specifying the expected type of the result.
ToString Returns a string that represents the current object.
(Overrides ObjectToString)

Extension Methods

Convert Converts an Object specified in the obj parameter to the desired targetType.
See the Examples section of the generic ConvertTTarget(Object, CultureInfo) overload for an example.
(Defined by ObjectExtensions)
ConvertTTarget Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions)
In Gets whether item is among the elements of set.
See the Examples section of the generic InT(T, T) overload for an example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvert Tries to convert an Object specified in the obj parameter to the desired targetType.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)
TryConvertTTarget Tries to convert an Object specified in the obj parameter to the desired TTarget.
See the Examples section of the ConvertTTarget(Object, CultureInfo) method for a related example.
(Defined by ObjectExtensions)

See Also