KGy SOFT

ResXResourceSet Class

KGy SOFT Core Libraries Help
Represents the complete content of an XML resource (.resx) file including resources, metadata and aliases.
See the Remarks section for examples and for the differences compared to System.Resources.ResXResourceSet class.
Inheritance Hierarchy

SystemObject
  System.ResourcesResourceSet
    KGySoft.ResourcesResXResourceSet

Namespace:  KGySoft.Resources
Assembly:  KGySoft.CoreLibraries (in KGySoft.CoreLibraries.dll) Version: 5.0.0-rc.1
Syntax

[SerializableAttribute]
public sealed class ResXResourceSet : ResourceSet, 
	IExpandoResourceSet, IDisposable, IEnumerable

The ResXResourceSet type exposes the following members.

Constructors

  NameDescription
Public methodResXResourceSet(Stream, String)
Initializes a new instance of the ResXResourceSet class using the ResXResourceReader to read resources from the specified stream.
Public methodResXResourceSet(TextReader, String)
Initializes a new instance of the ResXResourceSet class using the ResXResourceReader to read resources from the specified textReader.
Public methodResXResourceSet(String, String)
Initializes a new instance of a ResXResourceSet class using the ResXResourceReader that opens and reads resources from the specified file.
Top
Properties

  NameDescription
Public propertyAutoFreeXmlData
Gets or sets whether the raw XML data of the stored elements should be freed once their value has been deserialized.
Default value: .
Public propertyBasePath
Gets the base path for the relative file paths specified in a ResXFileRef object.
Public propertyCloneValues
Gets or sets whether GetObject/GetMetaObject and GetEnumerator/GetMetadataEnumerator methods return always a new copy of the stored values.
Default value: .
Public propertyFileName
If this ResXResourceSet has been created from a file, returns the name of the original file. This property will not change if the ResXResourceSet is saved into another file.
Public propertyIsModified
Gets whether this ResXResourceSet instance is modified (contains unsaved data).
Public propertySafeMode
Gets or sets whether the ResXResourceSet works in safe mode. In safe mode the retrieved objects are not deserialized automatically.
See the Remarks section for details.
Default value: .
Top
Methods

  NameDescription
Public methodClose
Closes and releases any resources used by this ResourceSet.
(Inherited from ResourceSet.)
Public methodContainsMeta
Gets whether the current ResXResourceSet contains a metadata with the given name.
Public methodContainsResource
Gets whether the current ResXResourceSet contains a resource with the given name.
Public methodDispose
Disposes of the resources (other than memory) used by the current instance of ResourceSet.
(Inherited from ResourceSet.)
Public methodEquals
Determines whether the specified object is equal to the current object.
(Inherited from Object.)
Public methodStatic memberFromFileContents
Creates a new ResXResourceSet object and initializes it to read a string whose contents are in the form of an XML resource file.
Public methodGetAliasEnumerator
Returns an IDictionaryEnumerator that can iterate through the aliases of the ResXResourceSet.
Public methodGetAliasValue
Gets the assembly name for the specified alias.
Public methodGetDefaultReader
Returns the type of ResXResourceReader, which is the preferred resource reader class for ResXResourceSet.
(Overrides ResourceSetGetDefaultReader.)
Public methodGetDefaultWriter
Returns the type of ResXResourceWriter, which is the preferred resource writer class for ResXResourceSet.
(Overrides ResourceSetGetDefaultWriter.)
Public methodGetEnumerator
Returns an IDictionaryEnumerator that can iterate through the resources of the ResXResourceSet.
(Overrides ResourceSetGetEnumerator.)
Public methodGetHashCode
Serves as the default hash function.
(Inherited from Object.)
Public methodGetMetadataEnumerator
Returns an IDictionaryEnumerator that can iterate through the metadata of the ResXResourceSet.
Public methodGetMetaObject
Searches for a metadata object with the specified name.
Public methodGetMetaString
Searches for a String metadata with the specified name.
Public methodGetObject(String)
Searches for a resource object with the specified name.
(Overrides ResourceSetGetObject(String).)
Public methodGetObject(String, Boolean)
Searches for a resource object with the specified name.
(Overrides ResourceSetGetObject(String, Boolean).)
Public methodGetString(String)
Searches for a String resource with the specified name.
(Overrides ResourceSetGetString(String).)
Public methodGetString(String, Boolean)
Searches for a String resource with the specified name.
(Overrides ResourceSetGetString(String, Boolean).)
Public methodGetType
Gets the Type of the current instance.
(Inherited from Object.)
Public methodRemoveAliasValue
Removes an assembly alias value from the current ResXResourceSet.
Public methodRemoveMetaObject
Removes a metadata object from the current ResXResourceSet with the specified name.
Public methodRemoveObject
Removes a resource object from the current ResXResourceSet with the specified name.
Public methodSave(Stream, Boolean, Boolean, String)
Saves the ResXResourceSet to the specified stream.
Public methodSave(TextWriter, Boolean, Boolean, String)
Saves the ResXResourceSet by the specified textWriter.
Public methodSave(String, Boolean, Boolean, String)
Saves the ResXResourceSet to the specified file.
Public methodSetAliasValue
Adds or replaces an assembly alias value in the current ResXResourceSet.
Public methodSetMetaObject
Adds or replaces a metadata object in the current ResXResourceSet with the specified name.
Public methodSetObject
Adds or replaces a resource object in the current ResXResourceSet with the specified name.
Public methodToString
Returns a string that represents the current object.
(Inherited from Object.)
Top
Extension Methods

  NameDescription
Public Extension MethodConvert(Type, CultureInfo)Overloaded.
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.)
Public Extension MethodCode exampleConvertTTarget(CultureInfo)Overloaded.
Converts an Object specified in the obj parameter to the desired TTarget.
(Defined by ObjectExtensions.)
Public Extension MethodIn (Defined by ObjectExtensions.)
Public Extension MethodIndexOf(FuncObject, Boolean)Overloaded.
Searches for an element in the source enumeration where the specified predicate returns .
(Defined by EnumerableExtensions.)
Public Extension MethodIndexOf(Object)Overloaded.
Searches for an element in the source enumeration.
(Defined by EnumerableExtensions.)
Public Extension MethodIsNullOrEmpty
Determines whether the specified source is  or empty (has no elements).
(Defined by EnumerableExtensions.)
Public Extension MethodTryAdd
Tries to add the specified item to the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryAddRange
Tries to add the specified collection to the target collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryClear
Tries to remove all elements from the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryConvert(Type, Object)Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)
Public Extension MethodTryConvert(Type, CultureInfo, Object)Overloaded.
Tries to convert an Object specified in the obj parameter to the desired targetType.
(Defined by ObjectExtensions.)
Public Extension MethodTryConvertTTarget(TTarget)Overloaded.
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.)
Public Extension MethodTryConvertTTarget(CultureInfo, TTarget)Overloaded.
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.)
Public Extension MethodTryGetElementAt
Tries to get an item at the specified index in the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryInsert
Tries to insert the specified item at the specified index to the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryInsertRange
Tries to insert the specified collection into the target collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryRemove
Tries to remove the specified item from to the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryRemoveAt
Tries to remove an item at the specified index from the collection.
(Defined by EnumerableExtensions.)
Public Extension MethodTryRemoveRange
Tries to remove count amount of items from the specified collection at the specified index.
(Defined by EnumerableExtensions.)
Public Extension MethodTryReplaceRange
Tries to remove count amount of items from the target at the specified index, and to insert the specified collection at the same position. The number of elements in collection can be different from the amount of removed items.
(Defined by EnumerableExtensions.)
Public Extension MethodTrySetElementAt
Tries to set the specified item at the specified index in the collection.
(Defined by EnumerableExtensions.)
Top
Remarks

Note Note
This class is similar to System.Resources.ResXResourceSet in System.Windows.Forms.dll. See the Comparison with System.Resources.ResXResourceSet section for the differences.
Tip Tip
To see when to use the ResXResourceReader, ResXResourceWriter, ResXResourceSet, ResXResourceManager, HybridResourceManager and DynamicResourceManager classes see the documentation of the KGySoft.Resources namespace.

The ResXResourceSet class represents a single XML resource file (.resx file) in memory. It uses ResXResourceReader internally to read the .resx content and ResXResourceWriter to save it.

A ResXResourceSet instance can contain resources, metadata and aliases (unlike the System.Resources.ResXResourceSet class, which contains only the resources). These contents are available either by enumerators (GetEnumerator, GetMetadataEnumerator and GetAliasEnumerator methods) or directly by key (GetString and GetObject methods for resources, GetMetaString and GetMetaObject for metadata, and GetAliasValue for aliases).

Example: Enumerating resources, metadata and aliases

The following example demonstrates how to access the content of a .resx file by the ResXResourceSet class using the enumerators. This is very similar to the first example of ResXResourceReader.

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, 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()
    {
        var set = new ResXResourceSet(new StringReader(resx));
        Console.WriteLine("____Resources in .resx:____");
        Dump(set, set.GetEnumerator);
        Console.WriteLine("____Metadata in .resx:____");
        Dump(set, set.GetMetadataEnumerator);
        Console.WriteLine("____Aliases in .resx:____");
        Dump(set, set.GetAliasEnumerator);
    }

    private static void Dump(ResXResourceSet set, Func<IDictionaryEnumerator> getEnumeratorFunction)
    {
        var enumerator = getEnumeratorFunction();
        while (enumerator.MoveNext())
        {
            Console.WriteLine($"Name: {enumerator.Key}");
            set.SafeMode = true;
            Console.WriteLine($"  Value in SafeMode:     {enumerator.Value} ({enumerator.Value.GetType()})");
            try
            {
                set.SafeMode = false;
                Console.WriteLine($"  Value in non-SafeMode: {enumerator.Value} ({enumerator.Value.GetType()})");
            }
            catch (Exception e)
            {
                Console.WriteLine($"Getting the deserialized value thrown an exception: {e.Message}");
            }
            Console.WriteLine();
        }
    }
}

 // The example displays the following output:
 // ____Resources in .resx:____
 // Name: string
 // Value in SafeMode:     Test string (KGySoft.Resources.ResXDataNode)
 // Value in non-SafeMode: Test string (System.String)

 // Name: int
 // Value in SafeMode:     42 (KGySoft.Resources.ResXDataNode)
 // Value in non-SafeMode: 42 (System.Int32)

 // Name: color
 // Value in SafeMode:     Red (KGySoft.Resources.ResXDataNode)
 // Value in non-SafeMode: Color[Red] (System.Drawing.Color)

 // Name: bytes
 // Value in SafeMode:     VGVzdCBieXRlcw== (KGySoft.Resources.ResXDataNode)
 // Value in non-SafeMode: System.Byte[] (System.Byte[])

 // Name: dangerous
 // Value in SafeMode:     YmluYXJ5 (KGySoft.Resources.ResXDataNode)
 // Getting the deserialized value thrown an exception: End of Stream encountered before parsing was completed.

 // ____Metadata in .resx:____
 // Name: meta string
 // Value in SafeMode:     Meta String (KGySoft.Resources.ResXDataNode)
 // Value in non-SafeMode: Meta String (System.String)

 // ____Aliases in .resx:____
 // Name: CustomAlias
 // Value in SafeMode:     System.Drawing, Version= 4.0.0.0, Culture= neutral, PublicKeyToken= b03f5f7f11d50a3a (System.String)
 // Value in non-SafeMode: System.Drawing, Version= 4.0.0.0, Culture= neutral, PublicKeyToken= b03f5f7f11d50a3a (System.String)

The ResXResourceSet class supports adding new resources (SetObject), metadata (SetMetaObject) and aliases (SetAliasValue). Existing entries can be removed by RemoveObject, RemoveMetaObject and RemoveAliasValue methods. The changed set can be saved by the Save overloads.

Example: Populating and saving a new resource set

The following example shows how to create a new resource set, add a new resource and save the content. It demonstrates the usage of the key-based resource access, too.

C#
 using System;
 using System.IO;
 using System.Text;
 using KGySoft.Resources;

 public class Example
 {
     public static void Main()
     {
         const string key = "myKey";
         var set = new ResXResourceSet();

         // GetString/GetObject: reads a resource by key (GetMetaString/GetMetaObject for metadata, GetAliasValue for alias)
         Console.WriteLine($"Getting a non-existing key: {set.GetString(key) ?? "<null>"}");

         // SetObject: adds a new resource or replaces an existing one (SetMetaObject for metadata, SetAliasValue for assembly alias)
         // you can even remove entries by RemoveObject/RemoveMetaObject/RemoveAliasValue)
         set.SetObject(key, "a string value");
         Console.WriteLine($"Getting an existing key: {set.GetString(key) ?? "<null>"}");

         var savedContent = new StringBuilder();
         set.Save(new StringWriter(savedContent), compatibleFormat: false); // try compatibleFormat: true as well
         Console.WriteLine("Saved .resx content:");
         Console.WriteLine(savedContent);
     }
}

 // The example displays the following output:
 // Getting a non-existing key: <null>
 // Getting an existing key: a string value
 // Saved .resx content:
 // <?xml version="1.0" encoding="utf-8"?>
 // <root>
 //   <data name="myKey">
 //     <value>a string value</value>
 //   </data>
 // </root>

If a .resx content contains the same resource name multiple times, ResXResourceSet will contain the lastly defined key. To obtain redefined values use ResXResourceReader explicitly and set AllowDuplicatedKeys to .

If the SafeMode property is  the value of the IDictionaryEnumerator.Value property returned by the enumerator methods is a ResXDataNode instance rather than the resource value. The same applies for the return value of GetObject and GetMetaObject methods. This makes possible to check the raw .resx content before deserialization if the .resx file is from an untrusted source. See also the example at ResXDataNode.

Security note Security Note
Even if SafeMode is , loading a .resx content with corrupt or malicious entry will have no effect until we try to obtain the corresponding value. See the example below for the demonstration.

If SafeMode property is  the GetString and GetMetaString methods will not throw an InvalidOperationException even for non-string values; they return the raw XML value instead.

Example: The SafeMode property

The following example demonstrates the behavior of SafeMode property (see the first example as well, where the entries are accessed by the enumerators).

C#
using System;
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 (when there is neither 'type' nor 'mimetype' attribute).</comment>
  </data>

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

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

    public static void Main()
    {
        // please note that default value of SafeMode is false. Nevertheless, reading the .resx containing an invalid node (dangerous)
        // will not cause any problem because nothing is deserialized yet.
        var set = ResXResourceSet.FromFileContents(resx); // same as "new ResXResourceSet(new StringReader(resx));"

        // enabling SafeMode changes the GetObject/GetString behavior
        set.SafeMode = true;

        Console.WriteLine($"Return type of GetObject in safe mode: {set.GetObject("string").GetType()}");

        Console.WriteLine();
        Console.WriteLine("*** Demonstrating SafeMode=true ***");
        TreatSafely(set, "unknown");
        TreatSafely(set, "string");
        TreatSafely(set, "binary");
        TreatSafely(set, "dangerous");

        set.SafeMode = false;
        Console.WriteLine();
        Console.WriteLine("*** Demonstrating SafeMode=false ***");
        TreatUnsafely(set, "unknown");
        TreatUnsafely(set, "string");
        TreatUnsafely(set, "binary");
        TreatUnsafely(set, "dangerous");
    }

    private static void TreatSafely(ResXResourceSet set, string resourceName)
    {
        // in SafeMode GetObject returns a ResXDataNode
        var resource = set.GetObject(resourceName) as ResXDataNode;
        if (resource == null)
        {
            Console.WriteLine($"Resource name '{resourceName}' does not exist in resource set or SafeMode is off.");
            return;
        }

        if (resource.TypeName == null && resource.MimeType == null)
        {
            // to deserialize a node considered safe call GetValue
            Console.WriteLine($"Resource with name '{resourceName}' is a string so it is safe. Its value is '{resource.GetValue()}'");
            return;
        }

        if (resource.TypeName != null)
        {
            Console.WriteLine($"Resource with name '{resourceName}' is a '{resource.TypeName}'. If we trust this type we can call GetValue to deserialize it.");
        }
        else
        {
            Console.WriteLine($"Resource with name '{resourceName}' has only mime type: '{resource.MimeType}'.");
            Console.WriteLine("  We cannot tell its type before we deserialize it. We can consider this entry potentially dangerous.");
        }

        // In SafeMode GetString(resourceName) never fails.
        // resource.ValueData is similar but ValueData can be null if we allow cleanup .resx content after deserialization.
        Console.WriteLine($"  Raw string value: {set.GetString(resourceName)}");
    }

    private static void TreatUnsafely(ResXResourceSet set, string resourceName)
    {
        // If SafeMode is false, GetObject returns null for existing resources containing null value, too.
        // Use ContainsResource to distinct non-existing and null values.
        if (!set.ContainsResource(resourceName))
        {
            Console.WriteLine($"The resource set does not contain a resource named '{resourceName}'.");
            return;
        }
        try
        {
            // If SafeMode is false, GetObject tries to deserialize the resource.
            // GetString would throw an InvalidOperationException on non-string values.
            var value = set.GetObject(resourceName);
            Console.WriteLine($"Type of resource with name '{resourceName}' is {value?.GetType().ToString() ?? "<none>"}. String representation: {value ?? "<null>"}");
        }
        catch (Exception e)
        {
            Console.WriteLine($"Obtaining '{resourceName}' failed with an error: {e.Message}");
        }
    }
}

// The example displays the following output:
// Return type of GetObject in safe mode: KGySoft.Resources.ResXDataNode
// 
// *** Demonstrating SafeMode=true ***
// Resource name 'unknown' does not exist in resource set or SafeMode is off.
// Resource with name 'string' is a string so it is safe. Its value is 'Test string'
// Resource with name 'binary' is a 'System.Byte[]'. If we trust this type we can call GetValue to deserialize it.
//   Raw string value: VGVzdCBieXRlcw==
// Resource with name 'dangerous' has only mime type: 'application/x-microsoft.net.object.binary.base64'.
//   We cannot tell its type before we deserialize it. We can consider this entry potentially dangerous.
//   Raw string value: boo!
// 
// *** Demonstrating SafeMode=false ***
// The resource set does not contain a resource named 'unknown'.
// Type of resource with name 'string' is System.String. String representation: Test string
// Type of resource with name 'binary' is System.Byte[]. String representation: System.Byte[]
// Obtaining 'dangerous' failed with an error: The input is not a valid Base-64 string as it contains a non-base 64 character,
// more than two padding characters, or an illegal character among the padding characters.

Comparison with System.Resources.ResXResourceSet 

ResXResourceSet can load .resx files produced both by ResXResourceWriter and System.Resources.ResXResourceWriter.

Note Note
When reading a .resx file written by the System.Resources.ResXResourceWriter class, the System.Windows.Forms.dll is not loaded during resolving System.Resources.ResXFileRef and System.Resources.ResXNullRef types.

Incompatibility with System.Resources.ResXResourceSet:

  • There are no constructors with single String and Stream arguments; though if using pure C# (without reflection) this is a compatible change as the second parameter of the constructors is optional.
  • The constructors of System.Resources.ResXResourceSet throw an ArgumentException on any kind of error, including when an object cannot be deserialized. This ResXResourceSet implementation does not deserialize anything on construction just parses the raw XML content. If there is a syntax error in the .resx content an XmlException will be thrown from the constructors. If an entry cannot be deserialized, the GetObject, GetMetaObject or ResXDataNode.GetValue methods will throw an XmlException, TypeLoadException or NotSupportedException based on the nature of the error.
  • This ResXResourceSet is a sealed class.

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

  • Supporting file references - If the .resx file contains file references with relative paths, then a base path can be defined in the constructors so the file references can be resolved successfully. See also the ResXFileRef class.
  • Full .resx support - A .resx file can contain also metadata and assembly alias entries in addition to resources and this ResXResourceSet implementation handles them.
  • Performance - Load time is much faster because the constructors just simply parse the raw XML content. The actual deserialization occurs on demand only for the really accessed resources and metadata. Memory footprint is tried to be kept minimal as well. If AutoFreeXmlData is , then raw XML data is freed after deserializing and caching an entry.
  • Security - This ResXResourceSet is much more safe, even if SafeMode is , because no object is deserialized at load time. If SafeMode is , then security is even more increased as GetObject and GetMetaObject methods, and the IDictionaryEnumerator.Value property of the enumerators returned by GetEnumerator and GetMetadataEnumerator methods return a ResXDataNode instance instead of a deserialized object so you can check whether the resource or metadata can be treat as a safe object before actually deserializing it. See the example above for more details.
  • Write support - The .resx file content can be expanded, existing entries can be replaced or removed and the new content can be saved by the Save methods. You can start even with a completely empty set, add content dynamically and save the new resource set.

See Also

Reference