views:

270

answers:

3

Basically I have a proof-of-concept application that is a digital recipe book. Each Recipe is an object and each object has, among other fields, a Vector containing arrays. The Vector is the list of all ingredients in the Recipe while each ingredient has an array showing the name of the ingredient, the amount, and the unit for that amount. I want to save each Recipe to XML so that they can be accessed by the user. How can I store a Vector of String arrays in XML or any other sort of file so that it can later be recalled and accessed?

+2  A: 

Why not model the ingredient as a real object as well? Then you have a sequence of Ingredient elements under Recipe, each with their own sub-elements or attributes of name, amount, unit etc..

gommo
Because of the way I want the program to work the Vector of arrays is much easier to implement. Mostly due to the fact that I don't know how many ingredients will be needed for each recipe. Ideally I'd like an answer to this question that does not require me to change the structure of my application.
hatboysam
You can make the xml schema accept n number of ingredients, you don't need to know it before hand. Encoding real domain objects inside of strings is a real code smell in my opinion.
gommo
A: 

To get off the ground quickly, you could use XStream. It produces nice output by default, and is very flexible so you can get pretty much what you want in the XML.

You can serialize an object to XML by writing

String xml = XStream.toXML(myObject);

As to the structure, you can continue to use vectors and lists when you don't know the quantity in advance, but it's better to use a list of domain objects (Recipes, Ingredients etc.) rather than string arrays. With XStream, you don't have to make everything a string to get it in XML.

If you really want to keep everything as it is with no changes, then the default XStream behaviour might not give you what you want. It doesn't know which Strings are recipes, ingredients etc. In this case, you have to write a bit more. For example, this code writes a Vector, as recipes with ingredients to the file "recipes.txt":

Vector<String[]> recipes = ... (your vector of recipes)
PrettyPrintWriter writer = new PrettyPrintWriter(new FileWriter("recipes.txt"));
writer.startNode("recipes");
for (String[] recipe: recipes)
{
   writer.startNode("recipe");
   // you might write out attributes, such as the recipe name
   for (String ingredient: recipe)
   {
      writer.startNode("ingredient");
      writer.setValue(ingredient);
      writer.endNode();
   }
   writer.endNode();
}
writer.endNode();
writer.close();

For the vector

Vector<String[]> v = new Vector<String[]>();
v.add("2 cups pasta", "1 onion");
v.add("2 eggs", "1.5 cups sugar", "1 tbsp butter");

the output will be

<recipes>
   <recipe>
      <ingredient>2 cups pasta</ingredient>
      <ingredient>1 onion</ingredient>
   </recipe>
   <recipe>
      <ingredient>2 eggs</ingredient>
      <ingredient>1.5 cups sugar</ingredient>
      <ingredient>1 tbsp butter</ingredient>
   </recipe>
</recipes>
mdma
+1  A: 

There are several techniques to generate XML, each with their own advantages and disadvantages. The simplest to implement (but the hardest to make right) is to build the XML manually.

Assuming you want your ingredient list to look something like:

<ingredients>
  <ingredient name="ginger"/>
  <ingredient name="cinnamon"/>
  <ingredient name="sugar"/>
</ingredients>

You need your code to walk the vector:

System.out.println("<ingredients>");
Vector ingredients;
for (String name : ingredients) {
  System.out.print("  <ingredient name=\"");
  System.out.print(name);
  System.out.println("\"/>");
}
System.out.println("</ingredients>");

The problem with such a method is that when you modify the underlying data structure (in your case, the Vector of Arrays, you need to modify the XML generation code to match (which often leads to incorrect XML).

It's much better to use an XML library to generate the XML. A good XML library will always generate valid XML.

import java.io.FileWriter;
import com.megginson.sax.XMLWriter;
import org.xml.sax.helpers.AttributesImpl;

public class GenerateXML
{

    public static void main (String args[])
    throws Exception
    {
    XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));

    writer.startDocument();
    writer.startElement("","ingredients","",null);
    for (String ingredient : ingredients) {
        AttributesImpl attribs = new AttributesImpl();
        attribs.addAttribute("","name","","",ingredient);
        writer.startElement("", "ingredient","",attribs);
        writer.endElement("ingredient");
    }
    writer.endElement("ingredients");
    writer.endDocument();
}

For a more complete description of what's possible with a DOM XMLWriter look at http://docstore.mik.ua/orelly/xml/sax2/ch02_02.htm with particular attention to Section 2.2.3.

You could also do a DOM to XML transformation, but using DOM means that you must build a DOM representation of your data structure, which probably is (in your case) an unnecessary and unwelcome extra step.

As an aside, while you may start off with something like a Vector of String arrays, such a data structure has little to do with the real world concepts of what a recipe entails. You might be better served in the long run to use a class Recipie, which contains classes for Ingredients, and Instructions (which in turn, contain steps). While it may seem like more work to lay out the three or four different types of classes, this work up front saves more than ten times the equivalent amount of work in getting the last 10% of the program done.

Even for a "quick" proof of concept, the time savings of using a truly descriptive data structure can be significant; as the proof of concept may quickly become the beginning prototype, which then might become the foundation for the real release.

Good luck, and happy programming!

Edwin Buck