Click here to Skip to main content
Click here to Skip to main content

XML: Generic File Writing and Loading Library

, 16 May 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
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

//
// 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 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:

GenericXml gx = new GenericXml();

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

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:

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:

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:

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:

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

Category / Entry / Item / Data Objects

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

// 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:

// 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:

// 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:

// 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:

// 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:

// 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.

// 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:

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:

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:

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:

public IGeneric findGenericImage ();

Find a specific child type=image element by key:

public IGeneric getImageByKey (String key);

Returns the direct children as IGeneric objects array:

public IGeneric[] getChildren (); 

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

public List<IGeneric> getAllChildren ();

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

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:

public IGeneric[] getGenericData ();

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

public List<IGeneric> matches (String regex);

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

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

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

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:

public String[] getTags ();

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

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

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:

public boolean delete (IGeneric object);

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

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:

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:

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.)

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 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

// 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

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

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

// 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

// 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

// 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

// 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

// 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)

Share

About the Author

j-hannemann
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 PinmemberNagy Vilmos12-May-11 4:27 
AnswerRe: My vote of 3 Pinmemberj-hannemann12-May-11 4:42 
GeneralRe: My vote of 3 PinmemberNagy Vilmos12-May-11 6:29 
AnswerRe: My vote of 3 Pinmemberj-hannemann13-May-11 4:23 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.141015.1 | Last Updated 16 May 2011
Article Copyright 2011 by j-hannemann
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid