Click here to Skip to main content
15,889,877 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
If I have a class with read-only properties that I only want to be created at constructor time e.g.:-

VB
Imports System.Runtime.Serialization

''' <summary>
''' An event to indicate that an FX rate was priced between two currencies
''' </summary>
<DataContract()>
Public Class FXRatePricedEvent
    Inherits AggregateIdentity.CurrencyExchangeAggregateIdentity
    Implements IEvent(Of IAggregateIdentity)

    <DataMember(Name:="Rate")>
    ReadOnly m_rate As Decimal
    <DataMember(Name:="PriceDate")>
    ReadOnly m_pricedate As Date
    <DataMember(Name:="IsDerived")>
    ReadOnly m_derived As Boolean


    ''' <summary>
    ''' The conversion rate between source and target currencies
    ''' </summary>
    ReadOnly Property Rate As Decimal
        Get
            Return m_rate
        End Get
    End Property

    ''' <summary>
    ''' The date/time the FX rate was priced
    ''' </summary>
    ReadOnly Property PriceDate As Date
        Get
            Return m_pricedate
        End Get
    End Property

    ''' <summary>
    ''' Is this rate derived from triangulation or reversal of a priced rate
    ''' </summary>
    ''' <remarks>
    ''' For example a derived GBPJPY can exist s if a priced GBPUSD and USDJPY exist
    ''' </remarks>
    ReadOnly Property IsDerived As Boolean
        Get
            Return m_derived
        End Get
    End Property

    Public Sub New(ByVal currencyFrom As AggregateIdentity.CurrencyAggregateIdentity.ISO_3Digit_Currency,
                   ByVal currencyTo As AggregateIdentity.CurrencyAggregateIdentity.ISO_3Digit_Currency,
                   ByVal fxRate As Decimal,
                   ByVal rateDate As Date,
                   ByVal derived As Boolean)
        ' Set the FX rate aggregate identifier
        MyBase.New(currencyFrom, currencyTo)
        ' and set the other properties
        m_rate = fxRate
        m_pricedate = rateDate
        m_derived = derived
    End Sub

End Class


Then System.Runtime.Serialization can serialise this to/from XML with no problem. However I want to do something similar but to a key-value pair dictionary....my attempts so to do hit a brick wall unless the class has a constructor with no parameters.

Any ideas how I (or they) get around this?
Posted
Comments
johannesnestler 7-Feb-14 4:53am    
the class you are showing has a default constructor (implemented by the compiler)... So you have a readonly dictionary and want to serialize it, seems I don't get it?
Duncan Edwards Jones 7-Feb-14 5:02am    
Basically yes - the System.Runtime.Serialization DataContractSerializer can take the above and turn it to XML and then recreate it from that XML.

..but how does it create the object it is deserializing given no public constructor it can use??
johannesnestler 7-Feb-14 5:08am    
There IS a public constructor in your example - you didn't write one with parameters so compiler will implement a default one for you, the other class you are talking about seems to have one with parameters - so there will be no default one - correct?
Duncan Edwards Jones 7-Feb-14 5:20am    
Yes - neither this class nor the inherited class have a constructor without parameters.

<pre lang="vb">
Public Sub New(ByVal currencyFrom As AggregateIdentity.CurrencyAggregateIdentity.ISO_3Digit_Currency,
ByVal currencyTo As AggregateIdentity.CurrencyAggregateIdentity.ISO_3Digit_Currency,
ByVal fxRate As Decimal,
ByVal rateDate As Date,
ByVal derived As Boolean)
' Set the FX rate aggregate identifier
MyBase.New(currencyFrom, currencyTo)
' and set the other properties
m_rate = fxRate
m_pricedate = rateDate
m_derived = derived
End Sub
</pre>

Currently I am saving it to XML then reading the XML into the name/value dictionary and vice versa but it is very clumsy...
johannesnestler 7-Feb-14 5:34am    
first example has a constructor - THE COMPILER HAS WRITTEN ONE, second clas doesn't because you have writte one with Parameters - then the COMPILER DOESN'T WRITE ONE. So just give it an internal Default constructor (have to write it yourself) if you don't want a public default one.

Example of using BinaryWriter, and BinaryReader to save a Dictionary<string,List<string>>:
C#
// required
using System.IO;

private Dictionary<string,List<string>> testDict = new Dictionary<string,List<string>>();

private string appPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + @"/dctStringString.bin";

private int itemsToCreate = 100000;

private int subListLength = 10;

private void btnTestDictionary_Click(object sender, EventArgs e)
{
    List<string> valueList;

    for (int i = 0; i < itemsToCreate; i++)
    {
        valueList = new List<string>();

        for (int j = 0; j < subListLength; j++)
        {
            valueList.Add(j.ToString());
        }

        testDict.Add(i.ToString(), valueList);
    }

    // serialize
    dctSerialize(testDict, appPath);

    // deserialize
    Dictionary<string,List<string>> deSerializedDict = dctDeserialize(appPath);
}

public void dctSerialize(Dictionary<string,List<string>> dctToWrite, string appPath)
{
    using (var writer = new BinaryWriter(File.Create(appPath)))
    {
        writer.Write(dctToWrite.Count);

        writer.Write(subListLength);

        foreach (var kvp in dctToWrite)
        {
            writer.Write(kvp.Key);
            for (int i = 0; i < subListLength; i++)
            {
                writer.Write(kvp.Value[i]);
            }
        }
        writer.Flush();
    }
}

public Dictionary<string,List<string>> dctDeserialize(string appPath)
{
    using (var reader = new BinaryReader(File.OpenRead(appPath)))
    {
        int itemsToRead = reader.ReadInt32();

        int subListLength = reader.ReadInt32();

        var dctToRead = new Dictionary<string,List<string>>(itemsToRead);

        for (int n = 0; n < itemsToRead; n++)
        {
            string key = reader.ReadString();
            
            List<string> value = new List<string>();

            for (int i = 0; i < subListLength; i++)
            {
                value.Add(reader.ReadString());
            }

            dctToRead.Add(key, value);
        }

        return dctToRead;
    }
}
This test generates a file with a million + 2 integers in it of about 2.46mb in size; if you zip the file, you can get it down to about 255k. Compressed with WinRar using standard settings yields a file under 70k.

It's on my list of things to do some day to polish this up and make it more "general purpose." Do things like making whether to write to a file, or memory, a parameter.

As you can see, if you start having generic Types as either Keys or Values in the Dictionary then you have to do some contortions; perhaps a real generic-type-master could find a way around that ?

I use this for speed, adapting it as necessary.

The code was adapted based on the discussion of timing, and techniques shown here: [^]; and, other sources that memory has been kind enough to erase :)

Disclaimer: If you use this code and cause famine, flood, power-grid failure, nuclear melt-down, plague epidemics, etc., please don't let me know, but if you find bugs, or see a better way to implement: I'm all ears !
 
Share this answer
 
v5
Hi again,

I can serialize this (sorry C# - my VB is a bit rusty...), so what is different in your case?

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;

namespace SerializeDictionary
{
    [DataContract]
    class Data
    {
        [DataMember]
        public string Value;
    }

    [DataContract]
    class Content
    {
        [DataMember(Name = "Dictionary")]
        readonly Dictionary<int, Data> m_dict = new Dictionary<int, Data>();

        public Content()
        {
            m_dict.Add(0, new Data { Value = "Data1" });
            m_dict.Add(1, new Data { Value = "Data3" });
            m_dict.Add(2, new Data { Value = "Data4" });
        }
    }

    static class Program
    {
        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            var content = new Content();
            DataContractSerializer serializer = new DataContractSerializer(typeof(Content));
            using (MemoryStream ms = new MemoryStream())
            {
                serializer.WriteObject(ms, content);
                ms.Position = 0;
                var readback = serializer.ReadObject(ms);
            }

            Console.ReadKey();
        }
    }
}
 
Share this answer
 
v2
Comments
BillWoodruff 7-Feb-14 5:50am    
Just curious: any potential problem de-serializing a ReadOnly Dictionary with these types for Key and Value ? No requirement to use the KnownTypes Attribute ?

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.knowntypeattribute.aspx
Duncan Edwards Jones 7-Feb-14 6:12am    
Ah - I didn't explain myself very well.
I want to serialise to/from a dictionary instead of to/from XML.
The class being serialized doesn't have any dictionaries in it.
BillWoodruff 7-Feb-14 6:17am    
Well, I'm confused; when you serialize, what format do you want the serialized content to be in ... in memory, or stored on disk ? Perhaps all you need is to use BinaryFormatter ?
Duncan Edwards Jones 7-Feb-14 6:21am    
Aha - maybe BinaryFormatter is what I'm looking for, yes.

Looking to store these as a set of name-value pairs in a database table...
BillWoodruff 7-Feb-14 6:29am    
So, encryption, and compression, are not issues ? Speed of access important ?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900