Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / Java

XML: Generic File Writing and Loading Library

Rate me:
Please Sign up or sign in to vote.
4.33/5 (3 votes)
16 May 2011CPOL8 min read 22.6K   349   14   4
Generic XML Tool to save and load XML files in a defined XML Format - useful for application settings or data

Introduction

This Generic XML Tool can be used to save and load XML files in a defined XML Format (including schema validation) and predefined functions. The library internally uses JAXB modelbinding and marshalling/unmarshalling for saving and loading.

Background

It's often useful to have an interface you may use in your applications to load and save settings or any data. With this tool, it's easy to build a schema defined structure, fill it with generic content and write/read it to/from file.

You can save mostly any kind of structured text data you want - no matter if it's a whole music database or contacts or just some settings of your application.

Using the Code

The XML file structure of possible elements is defined as shown below:

Xmldatafile (attributes: name) ? root
Category (attributes: key, value, type)
[ Tag (attributes: value) ]
[ Entry (attributes: key, value, type, imageKey for identifying a child containing a image file) ]
[ Data (attributes: key, value, type) ]
[ Tag (attributes: value) ]
[ Item (attributes: key, value, type) ]
[ Data (attributes: key, value, type) ]
[ Tag (attributes: value) ]

As you can see, only the 'Xmldatafile' root element and at least one 'Category' element are mandatory - everything else is optional.

Simple Usage Example

Java
//
// This is a simple example how the library can be used
//
GenericXml gx = new GenericXml();
IGeneric xmldata = gx.createNewDatamodel();

IGeneric cat = gx.createCategory(xmldata, "Manager", "John Blake", Type.TEXT);

IGeneric entry = gx.createEntry(cat, "Employee", "Susan Meyer", Type.TEXT, null);

IGeneric data = gx.createData(entry, "Salary", "4.653,00", Type.NUMBER);
gx.createTag(data, new String[] {"confidential"});

System.out.println(gx.toString()); // debug output

gx.saveData(new File ("c:\\temp\\generic.test.xml")); // save to file 

The result that has been saved to the XML file will look like this:

XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <xmldatafile name="xmldatadefault">
        <category key="Manager" value="John Blake" type="text">
                <entry key="Employee" value="Susan Meyer" type="text">
                        <data value="4.653,00" key="Salary" type="number"/>
                                <tag value="confidential"/>
                        </data>
                </entry>
        </category>
 </xmldatafile>

The stylesheet for this XML format is also included in the Jar Package.

GenericXml Method Descriptions

The main entry point for using this library is GenericXml. You can create and fill your data structure with that.

First, you have to create a new instance using:

Java
GenericXml gx = new GenericXml();

or if you'd like to set the belonging output and input filename:

Java
GenericXml gx = new GenericXml("c:\\temp\\datafile.xml");

Every GenericXml instance can be bound to a specific file. You may therefore want to use an autosave feature every time you use a create method:

Java
gx.setAutosave (true);  // default is false

If you activate the autosave but not set a filename, nothing will happen. You can explicitly set a filename by calling:

Java
gx.setFileName ("c:\\temp\\datafile.xml"); // to read it, call gx.getFilename();

To start building your structure, you must create a new data model / root entry:

Java
IGeneric xmldata = gx.createNewDatamodel();

Very important is the return type IGeneric because it's the main interface of this library.
You can optionally call the method with an name attribute to give your XML root a name.
This is just for your own information:

Java
IGeneric xmldata = gx.createNewDatamodel("contacts");

Category / Entry / Item / Data Objects

After that root is created, you may start by creating a category.

Java
// method definition
createCategory (IGeneric parent, String key, String value, IGeneric.Type type);

As you can see, you have to provide a parent - in this case, its our root 'xmldata' object.
You have to manage this for your own - the structure section at the beginning shows you which elements may contain certain other element types!

Then, as on most elements you have a 'key' and a 'value' - for example, to create contacts you may want to define contact groups.

So to create a group category, you could call:

Java
// create a contact group category (for example)
IGeneric familyGroup = gx.createCategory (xmldata, "Group", "Family", Type.TEXT);

The last attribute type is just in case you have to know if it's a number, text or filepath if you read this file later - if you don't need it, just put null into that parameter.

If everything went fine, you get the created IGeneric object, null if not.

The methods createEntry, createItem work the same but you have to create the right structure and pass a valid parent object to the methods - again refer to the structure at the beginning of the article.

To focus on this example, you could move on with the contacts and create an entry for each person:

Java
// create a person as an entry w/o imagekey (for example)
IGeneric personJohn = gx.createEntry (familyGroup, "Name", "John Evans", Type.TEXT, null);

Optionally

You may want to refer to a contact photo in your filesystem because you maybe want to load it directly and in this case you can specify a child 'key' that will contain the image filename by passing it directly as 5th attribute (null if you don't need it).

Example:

Java
// create a person as an entry with imagekey (for example)
IGeneric personJohn = gx.createEntry 
			(familyGroup, "Name", "John Evans", Type.TEXT, "photo");

The imageKey parameter is to get a direct reference to an child data item that you'll need at this level if you want to use it in your application.

To add such information, you can just add the element as Data:

Java
// create a data corresponding to the imageKey (for example)
IGeneric photoData = gx.createData 
	(personJohn, "photo", "c:\\contacts\\photos\\john.jpg", Type.IMAGE);

If you want to use this special structure, please make sure not to have more that one child key named 'photo'. )

Other (contact) data you would add as Data objects. Since you can add only tags to Data objects, you do not always have to save them. Example:

Java
// create a some data (for example)
gx.createData (personJohn, "phone", "04564/876331", Type.NUMBER);
gx.createData (personJohn, "street", "Road to nowhere 123", Type.TEXT);
gx.createData (personJohn, "birth", "17.07.1980", Type.TEXT);

Data can be seen as the lowest level of content.

In this example, we even didn't need an Item level - you do not have to use it as you can see.

Tagging

A Tag is a special element that you can add to any Category, Entry, Item or Data element to mark it - especially for intelligent searching purposes.

Java
// create a tag (for example) 
gx.createTag (personJohn, new String[] {"male", "no email"}); 

In the example above, I added two tags (ok, remember this is just to show you the functionality) to mark the Entry (in this case) with "male" and "no email" keywords.

Later, if your contact list would be filled with more data, you could maybe search all contacts that are "female" and all with "no email".

As a result, you'd get the parent elements (I will explain that below).

Saving and Loading

You can save and load your data structure manually by calling:

Java
gx.saveData();
gx.loadData();

If you haven't already set a filename, these calls won't do anything.

Additionally, you have the option to explicitly save and load your data to/from another file:

Java
gx.saveData(new File ("c:\\temp\\generic.test.xml"));
gx.loadData(new File ("c:\\temp\\generic.test.xml"));

In this case, the filename will not be saved and used for automatic saving or loading!

If you want to use that feature, please use setFilename before then you can use the methods without a parameter.

String Output

You may want to see your structure before writing it to file or for other reasons.

I did override the toString() method so you can call that directly on the GenericXml instance:

Java
System.out.println(gx.toString()); // output to console

The string representation will be recursively created and in this case written to the console.

IGeneric Methods

Every element of the data is an IGeneric object because it's an interface that all model classes implement.

Except the getters and setters for the key, values and types, there are some other useful methods described below.

Find the child element with type=image:

Java
public IGeneric findGenericImage ();

Find a specific child type=image element by key:

Java
public IGeneric getImageByKey (String key);

Returns the direct children as IGeneric objects array:

Java
public IGeneric[] getChildren (); 

Same as before, but returns recursively all child objects as List:

Java
public List<IGeneric> getAllChildren ();

Find / Delete child IGeneric objects matching a given 'key' (as RegEx) with option to search recursively:

Java
public List<IGeneric> findChildrenByKey (String key, boolean recursive);
public List<IGeneric> deleteChildrenWithKey (String key, boolean recursive);

You can get all Data child elements of an entry with one call as an array:

Java
public IGeneric[] getGenericData ();

To search recursively for a matching regular expression String in all child object keys and values:

Java
public List<IGeneric> matches (String regex);

Recursively find all objects that match one of the given tags (or operation):

Java
public List<IGeneric> findByTags(String[] tags);

So if you'd use it like with the example above:

Java
List<IGeneric> result = xmldata.findByTags (new String[] { "female" , "no email" } );

The result would contain all parent elements that have been tagged with either "female" or "no email".

To get the Tags for the object as String array (non-recursive), you may use:

Java
public String[] getTags ();

If you have no Tags, the array will be empty.

Deleting all direct child Tag objects (non-recursive):

Java
public List<IGeneric> deleteTags ();

As returned List, you'll get all deleted Tag objects.

Adding and Removing Objects

It's possible to add and delete objects on this IGeneric base.

I would recommend not to use the add method - please use the 'GenericXml' methods instead!

To remove a single object, you should use this one:

Java
public boolean delete (IGeneric object);

For example, to delete the person John from the contacts above, you could call:

Java
familyGroup.delete (personJohn); // returns true if the object has been found and removed

There is also a toString method with Boolean recursive parameter to get the string representation of the elements, for example:

Java
String result = personJohn.toString(true);

The method clone is not fully tested - in this version, I would recommend NOT to use it at this time.

Get the Parent Object

You have an extra feature that gives you the parent object of your current one.

You need to get that root Xmldatafile object for that one first, for example:

Java
Xmldatafile dataRoot = gx.getXMLDataFileObject();
IGeneric parent = dataRoot.getParentObject 
		(personJohn); // would return the category 'family'

Detailed Usage Example

Here is a more detailed example code to see how this library can be used:
(You may find this in the 'main' function in the GenericXml.java File.)

Java
GenericXml gx = new GenericXml();
// optional:
// gx.setAutosave(true);
// gx.setFileName("c:\\temp\\generic.test.xml");

IGeneric xmldata = gx.createNewDatamodel();

// Add a category
IGeneric cat = gx.createCategory(xmldata, "Manager", "John Blake", Type.TEXT);
gx.createTag(cat, new String[] {"sample", "managers", "male"});

IGeneric entry = gx.createEntry(cat, "Employee", "Susan Meyer", Type.TEXT, null);
gx.createTag(entry, new String[] {"sample", "employees", "female"});

IGeneric data = gx.createData(entry, "Salary", "4.653,00", Type.NUMBER);
gx.createTag(data, new String[] {"confidential"});
data = gx.createData(entry, "Age", "24", Type.NUMBER);

entry = gx.createEntry(cat, "Employee", "Joan Allen", Type.TEXT, null);
gx.createTag(entry, new String[] {"sample", "employees", "female"});

data = gx.createData(entry, "Age", "29", Type.NUMBER);
System.out.println("Data Tags: "+Arrays.deepToString(data.getTags()));

data = gx.createData(entry, "Salary", "3.787,00", Type.NUMBER);
gx.createTag(data, new String[] {"confidential"});

entry = gx.createEntry(cat, "Employee", "Tobias Low", Type.TEXT, null);
gx.createTag(entry, new String[] {"sample", "employees", "male"});

data = gx.createData(entry, "Age", "54", Type.NUMBER);
data = gx.createData(entry, "Salary", "8.126,00", Type.NUMBER);
gx.createTag(data, new String[] {"confidential"});

data = gx.createData(cat, "Age", "51", Type.NUMBER);
data = gx.createData(cat, "Salary", "9.356,00", Type.NUMBER);
gx.createTag(data, new String[] {"confidential"});

Content of XML file:

XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xmldatafile name="xmldatadefault">
    <category key="Manager" value="John Blake" type="text">
        <entry key="Employee" value="Susan Meyer" type="text">
            <data value="4.653,00" key="Salary" type="number">
                <tag value="confidential"/>
            </data>
            <data value="24" key="Age" type="number"/>
            <tag value="sample"/>
            <tag value="employees"/>
            <tag value="female"/>
        </entry>
        <entry key="Employee" value="Joan Allen" type="text">
            <data value="29" key="Age" type="number"/>
            <data value="3.787,00" key="Salary" type="number">
                <tag value="confidential"/>
            </data>
            <tag value="sample"/>
            <tag value="employees"/>
            <tag value="female"/>
        </entry>
        <entry key="Employee" value="Tobias Low" type="text">
            <data value="54" key="Age" type="number"/>
            <data value="8.126,00" key="Salary" type="number">
                <tag value="confidential"/>
            </data>
            <tag value="sample"/>
            <tag value="employees"/>
            <tag value="male"/>
        </entry>
        <data value="51" key="Age" type="number"/>
        <data value="9.356,00" key="Salary" type="number">
            <tag value="confidential"/>
        </data>
        <tag value="sample"/>
        <tag value="managers"/>
        <tag value="male"/>
    </category>
</xmldatafile>

Usage Demos

Java
// SEARCH DEMOS
List<IGeneric> l = xmldata.matches("Salary");
System.out.println("Search Result for 'Salary': "+l.size());
Iterator<IGeneric> iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString());

Output

Search Result for 'Salary': 4 Data: key=Salary/value=4.653,00/type=number Tag: name=confidential Data: key=Salary/value=3.787,00/type=number Tag: name=confidential Data: key=Salary/value=8.126,00/type=number Tag: name=confidential Data: key=Salary/value=9.356,00/type=number Tag: name=confidential

Java
l = xmldata.matches("2.");
System.out.println("\nSearch Result for '2.': "+l.size());
iter = l.iterator();
while (iter.hasNext()) {
    
    IGeneric o = iter.next();
    System.out.print(o.toString());
    IGeneric parent = ((Xmldatafile)xmldata).getParentObject(o);
    System.out.println(" <- Parent Name: "+parent.getGenericName());    
}

Output

Search Result for '2.': 2 Data: key=Age/value=24/type=number <- Parent Name: Susan Meyer Data: key=Age/value=29/type=number <- Parent Name: Joan Allen

Java
l = gx.getXMLDataFileObject().findByTags(new String[] { "confidential", "female"});
System.out.println("\nFind by Tag 'confidential', 'female': "+l.size());
iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString(false));

Output

Find by Tag 'confidential', 'female': 6 Data: key=Salary/value=4.653,00/type=number Entry: key=Employee/value=Susan Meyer/type=text/imageKey=null Data: key=Salary/value=3.787,00/type=number Entry: key=Employee/value=Joan Allen/type=text/imageKey=null Data: key=Salary/value=8.126,00/type=number Data: key=Salary/value=9.356,00/type=number

Java
// DELETIONS
//------------------------------------
// delete all 'Salary' Keys recursively
l = gx.getXMLDataFileObject().deleteChildrenWithKey("Salary", true);
System.out.println("\ndeleted salary objects:");
iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString(false));

// maybe save your work here... not in this example

// load unmodified version for further testing
gx.loadData(new File ("c:\\temp\\generic.test.xml"));

Output

deleted salary objects: Data: key=Salary/value=4.653,00/type=number Data: key=Salary/value=3.787,00/type=number Data: key=Salary/value=8.126,00/type=number Data: key=Salary/value=9.356,00/type=number Loading the file c:\temp\generic.test.xml

Java
// delete all 'confidential' Tags recursively starting at Manager
List<IGeneric> mgr = gx.getXMLDataFileObject().findChildrenByKey("Manager", false);
IGeneric manager = mgr.get(0); // first one - we have only one here in this example
l = manager.deleteChildrenWithKey("confidential", true);
System.out.println("\ndeleted confidential objects:");
iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString(false));

// maybe save your work here... not in this example

// load unmodified version for further testing
gx.loadData(new File ("c:\\temp\\generic.test.xml"));

Output

deleted confidential objects: Tag: name=confidential Tag: name=confidential Tag: name=confidential Tag: name=confidential Loading the file c:\temp\generic.test.xml

Java
// delete all children of Manager, except Employees
mgr = gx.getXMLDataFileObject().findChildrenByKey("Manager", false);
manager = mgr.get(0); // first one - we have only one here in this example
l = manager.deleteChildrenWithKey("[^E].*", false);
System.out.println("\ndeleted non-Employee direct children objects of Manager:");
iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString(false));        

// maybe save your work here... not in this example

// load unmodified version for further testing
gx.loadData(new File ("c:\\temp\\generic.test.xml"));

Output

deleted non-Employee direct children objects of Manager: Data: key=Age/value=51/type=number Data: key=Salary/value=9.356,00/type=number Tag: name=sample Tag: name=managers Tag: name=male Loading the file c:\temp\generic.test.xml

Java
// delete all children of Manager, except Employees
mgr = gx.getXMLDataFileObject().findChildrenByKey("Manager", false);
manager = mgr.get(0); // first one - we have only one here in this example
l = manager.deleteChildrenWithKey("[^E].*", false);
System.out.println("\ndeleted non-Employee direct children objects of Manager:");
iter = l.iterator();
while (iter.hasNext()) 
    System.out.print(iter.next().toString(false));

// maybe save your work here... not in this example

// load unmodified version for further testing
gx.loadData(new File ("c:\\temp\\generic.test.xml"));

Output

deleted non-Employee direct children objects of Manager: Data: key=Age/value=51/type=number Data: key=Salary/value=9.356,00/type=number Tag: name=sample Tag: name=managers Tag: name=male Loading the file c:\temp\generic.test.xml

Java
// delete all Tags of all Employees
mgr = gx.getXMLDataFileObject().findChildrenByKey("Employee", true);
List<IGeneric> delTags = new ArrayList<IGeneric> ();
Iterator<IGeneric> iMgr = mgr.iterator();
while (iMgr.hasNext()) 
    delTags.addAll(iMgr.next().deleteTags());
System.out.println("\ndeleted all Employee Tags:");
iMgr = delTags.iterator();
while (iMgr.hasNext())  
    System.out.print(iMgr.next().toString(false));

gx.saveData(new File ("c:\\temp\\generic.test.empl.wo.tags.xml"));

Output

deleted all Employee Tags: Tag: name=sample Tag: name=employees Tag: name=female Tag: name=sample Tag: name=employees Tag: name=female Tag: name=sample Tag: name=employees Tag: name=male saving the file to c:\temp\generic.test.empl.wo.tags.xml

History

  • Initial version with descriptions - 12. May, 2011

License

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


Written By
Database Developer
Germany Germany
I am programming more or less since I was 15 years.
It started with BASIC and during school I did
a lot Pascal programs for MS DOS.
After that I came to visual Windows programming and coded a lot in Delphi and later in C# .NET.
I made also some excursions in C and C++.

I also like Perl, Python and other scripting languages.
After a long break not programming anything, the last approx. 5-6 years I am only working with Java and SQL.

New for me is Objective-C - I am starting to get involved into Mac Programming Smile | :)

Comments and Discussions

 
GeneralMy vote of 3 Pin
Nagy Vilmos12-May-11 4:27
professionalNagy Vilmos12-May-11 4:27 
AnswerRe: My vote of 3 Pin
j-hannemann12-May-11 4:42
j-hannemann12-May-11 4:42 
GeneralRe: My vote of 3 Pin
Nagy Vilmos12-May-11 6:29
professionalNagy Vilmos12-May-11 6:29 
If you had the source in the zip then cpians will be able to browse without needing to download


Panic, Chaos, Destruction.
My work here is done.


or "Drink. Get drunk. Fall over." - P O'H

OK, I will win to day or my name isn't Ethel Crudacre! - DD Ethel Crudacre

Have a bit more patience with newbies. Of course some of them act dumb -- they're often *students*, for heaven's sake. -- (Terry Pratchett, alt.fan.pratchett)

AnswerRe: My vote of 3 Pin
j-hannemann13-May-11 4:23
j-hannemann13-May-11 4:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.