GithubHelp home page GithubHelp logo

fkorsa / migrations.json.net Goto Github PK

View Code? Open in Web Editor NEW

This project forked from weingartner/migrations.json.net

0.0 1.0 0.0 155.02 MB

A simple framework for data migrations using Newtonsoft Json.Net.

License: MIT License

PowerShell 2.26% C# 95.14% Batchfile 0.17% F# 2.43%

migrations.json.net's Introduction

Migrations.Json.Net

A simple framework for data migrations using Newtonsoft Json.Net.

Quickstart

Suppose you have the following class that gets serialized in your project:

  class MyData
  {
        string firstName = "John";
        string lastName = "Doe";
  }

But in a later revision, you change it to this:

  class MyData
  {
        string name = "John Doe";
  }

Oh noes! It's not backwards compatible. Well, with this library, you simply have to apply the following modifications:

  [Migratable("")]
  class MyData
  {
        string name = "John Doe";
        
        private static JObject Migrate_1(JObject data, JsonSerializer serializer)
	{
		data["name"] = data["firstName"] + " " + data["lastName"];
		return data;
	}
  }

And poof! When deserializing a json file with the previous format, it will automatically concatenate the previous firstName and lastName fields into the new 'name' field.

You do need to specify to Json.NET that you want some custom behavior to happen while deserializing:

string myData = ...; // Read from file?
var serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new MigrationConverter(new HashBasedDataMigrator<JToken>(new JsonVersionUpdater())));
MyData myDataDeserialized = JsonConvert.DeserializeObject<MyData>(myData, serializerSettings);

But that's all! If you want, you can stop reading.

Understanding the basics

Let's explain a bit what happens here.

  [Migratable("")]

This attribute marks the MyData class wrt. the migration system, so that it knows it needs to search for a migration method. The migration system does not parse classes that do not have this attribute.

  private static JObject Migrate_1(JObject data, JsonSerializer serializer)

The signature of the migration method should follow a specific format. First, it should be a static method on the class. It should also have the types defined above for its return type and arguments. Finally, its name should follow the following pattern: Migrate_number, where number is replaced by the version number of this class. A version number of 0 means that it's the original version of this class, and that there is no migration method necessary (hence the name "Migrate_0" is not possible). A version number greater than 0, e.g. n, means that the method is executed to migrate serialized data from version n - 1 to version n.

data["name"] = data["firstName"] + " " + data["lastName"];

Inside the method, you can modify the JObject that represents the deserialized data that comes from version n - 1 so that it can automatically be read by version n. In the example above, data would contain something that corresponds to this:

{
	"firstName": "John",
	"lastName": "Doe"
}

And this in the new format, what you want is this:

{
	"name": "John Doe"
}

That's why you need to add a "name" field to the data object, setting its value to the concatenated values of firstName and lastName.

NB: If you paid attention, you may have realized that the code above actually produces this:

{
	"name": "John Doe",
	"firstName": "John",
	"lastName": "Doe"
}

Indeed, we are not removing the existing "firstName" and "lastName" fields from the data object. But it doesn't matter! Because by default, Json.NET simply ignores json fields that do not appear in the deserialized class. If you are deserializing with a setting that makes Json.NET treat this as an error, you will want to remove those from the data object in the migration method.

Versioning

Each class has its own version field that is used by the migration library to know when to invoke which migration method. If a class doesn't define such a version field, the migration system will consider the class to be of version 0 - so it will invoke all the migration methods in order.

It means that in most cases, you will want to add this version field to your serialized classes. It takes the following form:

class MyData
{
	int Version = 1;
}

So an integer-typed member variable whose name is "Version".

The migration system will automatically increment the version field when deserializing. Let's take our initial example, but now adding the Version field:

  [Migratable("")]
  class MyData
  {
	int Version = 1;
	string name = "John Doe";
        
	private static JObject Migrate_1(JObject data, JsonSerializer serializer)
	{
		data["name"] = data["firstName"] + " " + data["lastName"];
		return data;
	}
  }

If this class is used to deserialize the following data:

{
	"firstName": "John",
	"lastName": "Doe"
}

then the result would be this:

{
	"Version": 1
	"name": "John Doe",
	"firstName": "John",
	"lastName": "Doe"
}

But it would be the same result if the loaded data had a Version field of value 0, e.g.:

{
	"Version": 0,
	"firstName": "John",
	"lastName": "Doe"
}

Because the migration method Migrate_1 would have parsed the Version field and incremented it to 1.

It follows that the Version field should be incremented each time your class is modified with changes that are backwards incompatible (otherwise you need to know what you're doing). Basically whenever you have the need to add a migration method, also remember to increment the Version field to the highest suffix of the migration methods. For instance, if you have the following migration methods: Migrate_1, Migrate_2, Migrate_3, then the Version field should have 3 as its default value, so that new instances of that class have the correct version number and do not invoke incorrect migration methods upon deserialization.

Advanced usage

This library supports nested classes as well, out of the box. Simply remember to mark each class as Migratable and to add a Version field to each class. Example:

  [Migratable("")]
  class Person
  {
	int Version = 1;
	string name = "John Doe";
	Address address = new Address();
        
	private static JObject Migrate_1(JObject data, JsonSerializer serializer)
	{
		data["name"] = data["firstName"] + " " + data["lastName"];
		return data;
	}
  }

  [Migratable("")]
  class Address
  {
	int Version = 1;
	string streetName = "Champs-Élysées";
        
	private static JObject Migrate_1(JObject data, JsonSerializer serializer)
	{
		data["streetName"] = data["street"]; // the field 'street' was renamed to 'streetName'
		return data;
	}
  }

The automatic tests in this repository demonstrate various use cases, it can be an additional source of inspiration if this readme is not explicit enough.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.