Section 1

Dec 24, 2016
Section 1
  • Basic Text

    If you remember back from the preface, we have a problem. No one is keeping track of the cats' medical and allergy information and we need some way of formatting and storing it. Let's try doing this with basic text.

    Remember that I said serialization is:

    We need a way of formatting a cat's information so it can be stored somewhere. Let's start by creating a container class that hold's a cat's information: (See here if you would like to see nicer looking code)
    Code (Java):

    public class PatientCat // We'll use this class to keep track of each cat patient's medical information
    {
        // Needed Fields
        private final UUID uuid; // Prevent collisions with other cats (Sometimes cat's will have the same name, odd isn't it?)
        private final String name; // Gives us an easier way of referring to a cat
        private final int age; // How old the cat is (in 'human years')
        private Allergy[] allergies; // An array containing the cat's allergy information.
        private MedicalNote[] medicalNotes; // An array containing the cat's medical information. (Can be broken bones, tumors, infections, etc.)

        //Pretty basic constructor. All we're doing here is creating a cat object with all the parameters we need.
        public PatientCat(UUID uuid, String name, int age, Allergy[] allergies, MedicalNote[] medicalNotes)
        {
            this.uuid = uuid;
            this.name = name;
            this.age = age;
            this.allergies = allergies;
            this.medicalNotes = medicalNotes;
        }

        // Getters (Allows us to get information from a cat object)

        public UUID getUUID()
        {
            return this.uuid;
        }

        public String getName()
        {
            return this.name;
        }

        public int getAge()
        {
            return this.age;
        }

        public Allergy[] getAllergies()
        {
            return this.allergies;
        }

        public MedicalNote[] getMedicalNotes()
        {
            return this.medicalNotes;
        }
    }
     
    Now let's create two classes that will contain the cats' allergies and medical notes:
    Code (Java):

    public class Allergy // This class is used for storing a cat's allergy information
    {
        // Needed Fields
        private final String description; // Describe the allergy
        private final int duration; // How long the allergy lasts in minutes

        // Basic constructor that accepts the values for the bits of information we need
        public Allergy(String description, int duration)
        {
            this.description = description;
            this.duration = duration;
        }

        // Getters

        public String getDescription()
        {
            return this.description;
        }

        public int getDuration()
        {
            return this.duration;
        }
    }
     
    Code (Java):

    public class MedicalNote
    {
        // Needed Fields
        private String description; // Describe exactly what's wrong (or what could go wrong) with the cat
        private boolean severe; // Whether or not the medical note is severe

        // Constructor that accepts the values we need
        public MedicalNote(String description, boolean severe)
        {
            this.description = description;
            this.severe = severe;
        }

        // Getters

        public String getDescription()
        {
            return this.description;
        }

        public boolean isSevere()
        {
            return this.severe;
        }
    }
     

    With that done, let's get to the serialization part. We have 5 pieces of information we need to save. The uuid, name, age, allergies, and medical notes. Somehow, we need to turn all this information into a single string. Let's create a method that will do this:
    Code (Java):

    public class PatientCat // We're going to be working in that same class we created above
    {
        // Just so you don't get confused, the constructor and fields are right above this comment. I've only omitted them so it's not as overwhelming

        public String serialize() // This method will actually turn all the cat's information into one big string
        {
            StringBuilder builder = new StringBuilder(); // Create a new StringBuilder so we aren't required to concatenate as much (each time we add two strings together, we're creating a new string object)
            // We'll begin by separating the fields we know won't vary (arrays can be different sizes)
            builder.append("%uuid%#").append(this.uuid).append("#"); // This is going to be our first field (uuid) Notice, that the key for the information (uuid) is surrounded by percent (%) signs and the actual value is surrounded by pound (#) signs
            builder.append("%name%#").append(this.name).append("#"); // Same thing above, except this time it's the cat's name
            builder.append("%age%#").append(this.age).append("#"); // This time it's the cat's age

            // Alright, now we have the arrays to work on. We'll serialize the arrays by separating each key-value pair by a comma ","
            builder.append("$allergies$&"); // Because we're working with an array, we need some way of letting our deserializer know it's an array, and so here we're denoting an array key with two dollar signs ($) (More on that when we work on the deserializer)
            for(int indexAllergy = 0; indexAllergy < this.allergies.length; indexAllergy++) // We're using an indexed for loop so we know when to separate key-value pairs with commas (,)
            {
                Allergy workingAllergy = this.allergies[indexAllergy]; // Grab a reference to the allergy we're currently working with

                builder.append("%description%#").append(workingAllergy.getDescription()).append("#"); // Same as we did above with the uuid, name, and age; except, it's inside the serialized array we're currently creating
                builder.append("%duration%#").append(workingAllergy.getDuration()).append("#"); // Again, the same thing as we did before

                if(indexAllergy != (this.allergies.length - 1)) // Check if there are more values in the array
                {
                    builder.append(","); // We're going to add a comma because there is still more allergies to add into the serialized array
                }
            }
            builder.append("&"); // Let's our deserializer know our array has ended



            // Time for the medical notes (We're doing almost exactly the same thing we did as above
            builder.append("$medicalNotes$&"); // Let our deserializer know it's working with an array
            for(int indexMedicalNote = 0; indexMedicalNote < this.medicalNotes.length; indexMedicalNote++) // We're using an indexed for loop so we know when to separate key-value pairs with commas (,)
            {
                MedicalNote workingMedicalNote = this.medicalNotes[indexMedicalNote]; // Grab a reference to the medical note we're currently working with

                builder.append("%description%#").append(workingMedicalNote.getDescription()).append("#"); // Both allergies and medical notes have a description (it's the exact same thing as we did before)
                builder.append("%severe%#").append(workingMedicalNote.isSevere()).append("#"); // Again, the same thing as we did before except we're using the 'severe' key with a boolean value

                if(indexMedicalNote != (this.medicalNotes.length - 1)) // Check if there are more values in the array
                {
                    builder.append(","); // We're going to add a comma because there is still more medical notes to add into the serialized array
                }
            }
            builder.append("&"); // Let's our deserializer know our array has ended

            // Alright, we've built our serialized array. Let's return it
            return builder.toString(); // It's ready to be stored/sent/deserialized
        }

        // Getters will be below this comment. Omitted so the code isn't as overwhelming
    }
     
    The code above may look extremely confusing, even despite the many comments I placed. If you feel this way, don't worry, I do too. In a moment, we're going to discuss the situation in greater detail; until then, just bare with me! :D

    Now that we have a method that turns a cat into a string, we'll need a method that will let us turn it back into an object. Before we do that though, I wanted you to see what the string looks like when you serialize a cat object:

    Code (Text):

    %uuid%#71445d04-3aee-4fe9-91d7-82ac3067aebc#%name%#Tom#%age%#3#$allergies$&%description%#Allergic to eggnog#%duration%#139#,%description%#Allergic to other cats#%duration%#245#&$medicalNotes$&%description%#Fractured bone#%severe%#true#,%description%#Stomach Ache#%severe%#false#&
     
    Looks pretty ambiguous doesn't it? That's okay, we want this information to be lightweight, and easy to serialize. We're not going to be reading it like this. Here, serialization has been demonstrated. We've turned an ordinary cat object into a giant string that contains all that cat's information.

    Anyways, let's get back to deserializing. Rather than using a method (which would have to be accessed statically, or by a factory class object) we'll create another constructor in the PatientCat class for simplicity:
    Code (Java):

    public PatientCat(String serializedPatientCat) // We'll use this constructor to accept a serialized cat string and build a cat object with it
    {
        boolean read = false; // Whether or not characters should be serialized to the builder
        boolean array = false; // Let our deserializer know if we're reading an array

        ArrayList<String> listArray = new ArrayList<>(); // Contains the 5 fields we have (with the last 2 still being serialized)
        StringBuilder builder = new StringBuilder(); // Used for building each string that is put into the array

        for(char character : serializedPatientCat.toCharArray()) // Loop through each character in the serialized string array
        {
            if(character == '&') // if the character is the ampersand (&) symbol, we're expecting to toggle array mode
            {
                if(array) // If we're in the array it means we've ended it now
                {
                    listArray.add(builder.toString());  // So let's add what we have now (which is still a serialized string)
                    builder.delete(0, builder.length()); // Clear the builder
                }
                array = !array; // Toggle the array as being on or off
                continue;
            }
            if(character == ',') // If we're in array mode, we'll see a comma separating multiple key-value pairs, so we need to preserve this serialization method for what we're going to do later
            {
                builder.append("|"); // Append a pipe symbol (|) just so we know how to interpret the format later
                continue;
            }
            if(character == '#') // Handle values
            {
                if(read) // If we're already reading, it means we're doing reading this value
                {
                    if(array) // If we're in array mode let's add a comma to separate the two fields
                    {
                        builder.append(","); // Add a comma to separate the two fields
                    }
                    else // Otherwise, we'll just add the value to the array
                    {
                        listArray.add(builder.toString()); // Add the value
                        builder.delete(0, builder.length()); // Clear the builder
                    }
                }
                read = !read; // Toggle reading mode as on or off
                continue;
            }
            if(read) // If we're reading, add the character to the string builder
            {
                builder.append(character); // Add the character to the builder
            }
        }

        this.uuid = UUID.fromString(listArray.get(0)); // We know that the first value will be the UUID because of how we serialized it
        this.name = listArray.get(1); // Same for the name except it's the second value
        this.age = Integer.parseInt(listArray.get(2)); // Same, but third value

        String[] serializedAllergies = listArray.get(3).split("\\|"); // Let's split that serialized array of allergies so we can start determining the field's values
        Allergy[] allergies = new Allergy[serializedAllergies.length]; // We know how many allergies we have (because the pipe (|) separates the key-value pairs) so let's create an array that we'll give to this object after we're done

        for(int index = 0; index < serializedAllergies.length; index++) // Loop through each allergy key-value-pair
        {
            String[] allergyFields = serializedAllergies[index].split(","); // Split them with a comma (the first value will be the description, the second the duration)
            allergies[index] = (new Allergy(allergyFields[0], Integer.parseInt(allergyFields[1]))); // Create a new allergy with its appropriate values and then set it at the correct index in the array
        }

        this.allergies = allergies; // Assign the array we found through deserialization to the class's reference


        String[] serializedMedicalNotes = listArray.get(4).split("\\|"); // Split the serialized array of medical notes so we can determine the field values
        MedicalNote[] medicalNotes = new MedicalNote[serializedAllergies.length]; // Again, we know how many key-value pairs of medical notes there are, so we can create an array we'll give to this class

        for(int index = 0; index < serializedMedicalNotes.length; index++) // Loop through each medical note key-value-pair
        {
            String[] medicalNoteFields = serializedMedicalNotes[index].split(","); // Split them with a comma (the first value being the description, and the second being if it's severe or not)
            medicalNotes[index] = (new MedicalNote(medicalNoteFields[0], Boolean.parseBoolean(medicalNoteFields[1]))); // Create a new medical note with the appropriate values and then set it at the correct index in the array
        }

        this.medicalNotes = medicalNotes; // Assign the array we found through deserialization to the class's reference
    }
     
    Again, I know it looks confusing, and if you still don't get it, that's completely okay, just keep following along.

    Now that we have a way to serialize and deserialize our data, let's see it in action:
    Code (Java):

    Allergy[] allergies = new Allergy[2]; // Create an array that hold's this cat's allergies
    allergies[0] = new Allergy("Allergic to eggnog", 139); // reference index 0 to this allergy
    allergies[1] = new Allergy("Allergic to other cats", 245); // reference index 1 to this allergy

    MedicalNote[] medicalNotes = new MedicalNote[2]; // Create an array that hold's this cat's medical notes
    medicalNotes[0] = new MedicalNote("Fracture bone", true); // reference index 0 to this medical note
    medicalNotes[1] = new MedicalNote("Stomach Ache", false); // reference index 1 to this medical note

    UUID randomUUID = UUID.randomUUID(); // Create a random UUID (just for tutorial purposes)

    PatientCat patientCat = new PatientCat(randomUUID, "Tom", 3, allergies, medicalNotes); // Create the cat (uuid, name, age, allergies, and medical notes)

    String serializedCat = patientCat.serialize(); // Serialize this cat into a String using that serialization method we created

    System.out.println(patientCat.getName() + " has been serialized into: "); // Let us know that the cat has been serialized
    System.out.println(serializedCat); // Print out what the serialized cat looks like


    PatientCat deserializedCat = new PatientCat(serializedCat); // Let's deserialize the cat and create a new cat object

    System.out.println("We found: " + deserializedCat.getName() + " being " + deserializedCat.getAllergies()[0].getDescription()); // Show us if it worked or not by printing the cat's name and one if its allergies
     
    Okay. Let's take a deep breath. :oops:

    Alright. What even did we do? In these examples, I demonstrated the use of creating your own serialization and deserialization method. I showed you how to turn a cat object into a string, and then back into a cat object using that string. The methods we used, however, were fairly complicated, weren't they?

    This is a point I would like to illustrate before we look at any other serialization methods. You see, the methods we wrote can take a lot of time to come up with. We have to consider how to abstract the data and serialize it into a format that we define. And then, we need to decide how we'll deserialize it, keeping in mind that the serialized format could be incorrect.

    What we wrote was very simple and the methods we wrote are flawed in a few ways. One being, that we aren't handling the use of special characters in the allergy and medical note descriptions. What if someone typed '#' everywhere? It wouldn't work when it comes time to deserialize it. :eek: A lot more ideas need to be considered when creating your own serialization format, which can be quite time consuming.


    "Alright, so I should never write my own methods then." Well, maybe. See, it depends. Even though this method can be very confusing, it gives you great power. You have the ability to define exactly how your objects will be serialized. When you're using a method designed by someone else, however, you have to do it their way, which isn't good in some cases.

    Hopefully by now, you understand what serialization is, and how complicated it can be to come up with your own methods. If you didn't understand how we serialized a cat object, but understand what serialization is, then you're in great shape. I really wanted you to understand what serialization is by demonstrating it's practice. Knowing how we did it isn't as important.

    Here's a few notes that I'd like you to keep in mind when considering this as an option for serializing your data.


    This method should be used for:
    When you need a very specific format to store/move your data. If other formats just don't work for you, and you're stuck, then I recommend you create your own. It will be difficult but in the end it will help you accomplish your goals. If you're really a hard-core learner, then I'd also recommend you take a wack at creating your own serialization format/methods. It can be quite fun! ;)

    Pros of using this method:

    • Define exactly how your data is serialized
    • Great learning experience

    Cons of using this method:
    • Difficult to setup
    • Hard to understand by users unfamiliar with your format
    • Support isn't as high as it would be if you used a more defined serialization format
  • Loading...
  • Loading...