XBean > Home > Features > Custom XML

One of the main points of using XML rather than, say, a script, to configure your application is that it allows any XML tooling to edit the configuration. We really like Spring's XML format, however it is very technical - folks need to understand the code to be able to configure the application.

What we'd like is an alternative where components can have their own namespaces and schemas which are more concise and work better in XML editing tools.

There follows a number of examples on different ways of using custom XML syntaxes; in each case the main requirement is you use the XBean versions of the usual Spring ApplicationContext classes. e.g. use the org.xbean.spring.context.ClassPathXmlApplicationContext class instead of the Spring base class.

For more information on editing the XML see Editing Custom XML.

Simple example

The simplest example uses a custom namespace starting with java:// and ending with the Java package name to denote the package/classes to use in the XML. This avoids needing any special mapping configuration and can work with most POJOs today...

<beans xmlns:p="java://org.apache.xbean.spring.example">

  <p:PizzaService id="pizzaService" topping="Salami" cheese="Edam" size="17"/>
  
</beans>

The Java POJO which this configures is shown below. Notice in the above that the element localName is the same as the class in the package from the namespace. So this mechanism works great when there is a one to one mapping of XML element names to class names in the package defined by the java:// namespace.

public class PizzaService {

    private static final Log log = LogFactory.getLog(PizzaService.class);
    
    private String topping;
    private String cheese;
    private int size;
    private double price;

    public void makePizza() {
        log.info("Making a pizza with topping: " + topping + " cheese: " + cheese + " with size: " + size);
    }

    public String getCheese() {
        return cheese;
    }

    public void setCheese(String cheese) {
        this.cheese = cheese;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    /**
     * @org.apache.xbean.Property alias="myTopping"
     */
    public String getTopping() {
        return topping;
    }

    public void setTopping(String topping) {
        this.topping = topping;
    }

}

Custom XML mappings

This example shows how to customise the mapping of XML to POJOs using a discovery-properties file. Using the same POJO as above, here's an example of the XML

<beans xmlns:p="http://xbean.apache.org/schemas/pizza">

  <p:pizza id="pizzaService" myTopping="Salami" cheese="Edam" size="17"/>
  
</beans>

Notice that we are using our own custom namespace here and using a custom element name too. To inform the XBean XML parser of your new custom mapping you need to create a properties file on the classpath at META-INF/services/org/apache/xbean/spring/$namespace. The namespace is encoded to take out : and // etc.

So in the above example we need to include this properties file in META-INF/services/org/apache/xbean/spring/http/xbean.apache.org/schemas/pizza. The contents of the file are here

# the default package that POJOs are in
package = org.xbean.spring.example

# Mapping of XML Element localNames to classes 
pizza = org.xbean.spring.example.PizzaService
restaurant = org.xbean.spring.example.RestaurantService

# Mapping of XML Attributes to property names
pizza.myTopping = topping

# Mapping of nested bean properties
restaurant.dinnerMenu.list = dinnerMenu
restaurant.favourite = favourite

Notice that we are renaming both XML element names to classes and XML attribute names to different property names. The renaming of properties can be useful to avoid clashes with standard Spring attributes like "id", "class", "ref" etc.

Constructor injection

This example shows how to customise the mapping of XML to POJOs which use constructor injection. For this example, we are going to configure the following bean which can only be configured using constructor injection:

public class SaladService {
    private final String dressing;
    private final String size;
    private final boolean crouton;

    public SaladService(String dressing, String size, boolean crouton) {
        this.dressing = dressing;
        this.size = size;
        this.crouton = crouton;
    }

    /**
     * Dressing What type of dressing do you want?
     */
    public String getDressing() {
        return dressing;
    }

    /**
     * What size do you want?
     */
    public String getSize() {
        return size;
    }

    /**
     * Do you want crutons on that?
     * @org.apache.xbean.Property alias="addCroutons"
     */
    public boolean isCrouton() {
        return crouton;
    }
}

The Xml uses the same clean format as above:

<beans xmlns:s="http://xbean.apache.org/schemas/salad">

  <s:salad id="saladService" dressing="Cesar" size="Small" addCroutons="true"/>
  
</beans>

In this example we use a custom namespace just like we did in the previous example. To enable constructor injection we need to provide the constructor argument names to the XBean Spring parser in the mapping properties file as follows:

# the default package that POJOs are in
package = org.apache.xbean.spring.example

# Mapping of XML Element localNames to classes 
salad = org.apache.xbean.spring.example.SaladService

# Mapping of XML Attributes to property names
salad.alias.addCroutons = crouton

# Mapping of constructor argument names
org.apache.xbean.spring.example.SaladService(java.lang.String,java.lang.String,boolean).parameterNames=dressing size crouton

The most important element of this file is the last entry, which tell the parser that the SaladService(java.lang.String,java.lang.String,boolean) constructor parameters are named dressing, size and crouton respectively.

Handling nested properties which are beans or list of beans

Its quite common to want to use nested child elements to map to complex property values. The following example shows this in action...

<beans>

  <restaurant id="restaurant" xmlns="http://xbean.apache.org/schemas/pizza" xmlns:foo="http://acme.com" serviceName="foo:xyz" uri="http://cheese.com">
    <dinnerMenu>
      <pizza myTopping="Ham" cheese="Mozzarella" size="15"/>
      <pizza myTopping="Eggs" cheese="Mozzarella" size="16"/>
    </dinnerMenu>

    <lunchMenu>
      <pizza myTopping="Chicken" cheese="Brie" size="17"/>
    </lunchMenu>

    <favourite>
      <pizza myTopping="Salami" cheese="Edam" size="17"/>
    </favourite>
  </restaurant>
</beans>

Notice that the nested element <dinnerMenu> maps to the collection of pizza beans which maps to a Spring <property name="foo"><list>... construct. This can be achived using the pizza.dinnerMenu.list = realPropertyName entry in the properties file. If there is no entry for the nested property element then introspection is used on the class to determine if its a <property><bean>... or <property><list><bean>... style property.

Also we can handle non-list nested properties, such as the <favourite> element which maps to a <property name="foo">... construct. Again XBean will default to using introspection if necessary, otherwise you can configure this using the pizza.favourite = realPropertyName construct.

More tools

If you want to automatically generate HTML documentation for your XML configuration or to auto-generate the META-INF/services/* properties file or to make an XSD for your XML then please checkout the XBean Ant Task