Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I make Raven DB serialize an object as it would a string, if i have created an implicit type conversion operator?

I have a class that looks something like this:

public class MyClass
{
    string _value;

    public static implicit operator MyClass (string value)
    {
        return new MyClass(value);
    }

    MyClass(string value)
    {
        // Do something...
        _value = value;
    }

    public override string ToString()
    {
         // Do something...
         return _value;
    }
}

Hence, I can use the class like this:

MyClass a = "Hello!";

But in Raven DB it will just be stored like

"SomeProperty": {}

since it has no public properties. And it is quite useless.

To solve this I would make the _value private member a public property instead, like this:

public string Value { get; set; }

and Raven DB will store

"SomeProperty": { "Value": "Hello!" }

and it will be deserializable.

But I don't want this public property. Can I somehow make Raven DB serialize and deserialize the class as was it would a string? Like:

"SomeProperty": "Hello!"
like image 238
Mikael Avatar asked May 17 '12 19:05

Mikael


2 Answers

Hi I know this is old but I thought I would add some additions to Ayendes' reply to help people who like me had the same issue and spent hours looking on forums for an answer (of which there were a few but none had any example that you could follow), it's not hard to figure this out but with an example I could have solved this in 10 minutes as opposed to spending a few hours.

My problems was that we have custom value type structs in our application the example I will use is EmailAddress. Unfortunately in Ravendb we could not run queries against these types without defining a custom serialiser.

Our Value Type looked Like this:

[DataContract(Namespace = DataContractNamespaces.ValueTypes)]
public struct EmailAddress : IEquatable<EmailAddress>
{
    private const char At = '@';

    public EmailAddress(string value) : this()
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        this.Value = value;
    }

    public bool IsWellFormed
    {
        get
        {
            return Regex.IsMatch(this.Value, @"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
        }
    }

    public string Domain
    {
        get
        {
            return this.Value.Split(At)[1];
        }
    }

    [DataMember(Name = "Value")]
    private string Value { get; set; }

    public static bool operator ==(EmailAddress left, EmailAddress right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(EmailAddress left, EmailAddress right)
    {
        return !left.Equals(right);
    }

    public override bool Equals(object obj)
    {
        if (obj == null)
        {
            return false;
        }

        return this.Equals(new EmailAddress(obj.ToString()));
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }

    public override string ToString()
    {
        return this.Value;
    }

    public bool Equals(EmailAddress other)
    {
        return other != null && this.Value.Equals(other.ToString(), StringComparison.OrdinalIgnoreCase);
    }
}

The type of document we wanted to save and query would look something like this

public class Customer
{
    public Guid Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public EmailAddress Email { get; set; }
}

The custom serialiser to store our email as a raw string and then convert it back to its value type on retrieval looked like this:

public class EmailConverterTest : JsonConverter
{

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(EmailAddress);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        EmailAddress actualAddress =  new EmailAddress(reader.Value.ToString());

        return actualAddress;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        EmailAddress actualAddress = (EmailAddress)value;
        string stringEmail = actualAddress.ToString();
        writer.WriteValue(stringEmail);
    }
}

Finally I wired it up and was able to query everything as follows:

    public static void serializercustom(Newtonsoft.Json.JsonSerializer serialiser)
    {
        serialiser.Converters.Add(new EmailConverterTest());
    }

    public static void TestCustomer()
    {
        using (var documentStore = new DefaultDocumentStore())
        {
            documentStore.ConnectionStringName = Properties.Settings.Default.SandBoxConnection;
            documentStore.Initialize();
            documentStore.Conventions.CustomizeJsonSerializer = new Action<Newtonsoft.Json.JsonSerializer>(serializercustom);

            var customer = new Customer
            {
                Id = Guid.NewGuid(),
                FirstName = "TestFirstName",
                LastName = "TestLastName",
                Email = new EmailAddress("[email protected]")
            };

            // Save and retrieve the data
            using (var session = documentStore.OpenSession())
            {
                session.Store(customer);
                session.SaveChanges();
            }

            using (var session = documentStore.OpenSession())
            {
                var addressToQuery = customer.Email;
                var result = session.Query<Customer>(typeof(CustomerEmailIndex).Name).Customize(p => p.WaitForNonStaleResults()).Where(p => p.Email == addressToQuery);

                Console.WriteLine("Number of Results {0}", result.Count()); // This always seems to return the matching document
            }
        }
    } 
like image 160
Jesse Avatar answered Nov 14 '22 21:11

Jesse


You can write a JsonConverter and teach RavenDB how you want to store the data. After you write the converter, register it in the store.Conventions.CustomizeSerializer event.

like image 36
Ayende Rahien Avatar answered Nov 14 '22 22:11

Ayende Rahien