devxlogo

Clean Up Your Schema for SOAP

Clean Up Your Schema for SOAP

f you’ve built a library of schemas, you might want to reuse them for new applications. If you already have a data model for an internal purchase order, as you move towards Web services, you may need to update it for use with SOAP. SOAP allows you to transport an XML message across a network and the XML body can also be constrained with a schema. However, a SOAP message typically uses element data for its XML body, not attribute data. We’ll explore a program that can automatically update an existing schema document to convert any attribute declaration into roughly “equivalent” element declarations.

Overview: How We’re Going to Do It
Given the complexity of XML Schemas, we certainly don’t want to use Notepad to edit the .xsd files. A good XML editor is not much of a step up: while it may organize our elements and attributes nicely, it can’t show the many abstract Infoset relationships that are defined in the Schema specification. That’s where the Schema Infoset Model comes in: it expresses both the concrete DOM representation of a set of schema documents, and the full abstract Infoset model of a schema. Both of these representations are shown through the programmatic API of the Model as well as in the Model’s built-in sample editor for schemas.

This article assumes a basic understanding of schemas. The sample code included in our zip file works as a standalone or in an Eclipse workbench.

If you’ve installed the XSD Schema Infoset Model and EMF Eclipse Modeling Framework plug-ins into Eclipse, you can see the sample editor at work in your Workbench. (Note: Exploring the editor is not required to follow this article). Simply right-click on a schema.xsd file in the Navigator, and select Open With… Sample XML Schema Editor. You will get a standard Eclipse editor that shows the usual Source view?this is the concrete DOM representation of the .xsd file that you just opened.

At the bottom of the editor there are two more tabs after the Source view?Semantics and Syntax. These two graphical tree views show the various abstract Infoset relationships between schema components. For example in the Semantics view, you will see a top-level item for Types?this is all of the types (simple and complex) declared anywhere in the schema itself, not just at the top level, and not just in this document (this becomes more obvious when the schema document you opened uses includes and imports).

Figure 1: Changing Attributes into Elements.

For the purposes of our example, we’re going to simplify our problem slightly. MakeSoapCompatible.java is a program that will attempt to take most attributeDeclarations in a schema and turn them into roughly-equivalent elementDeclarations, with a few caveats.

First, we obviously can’t convert an attribute into an element if there already is an element with that name in the particles of the complexType: we will detect name conflicts first, and refuse to change these attributes. To keep our example simpler, we also arbitrarily declare some other conditions that make a schema incompatible for our program. We won’t change schemas that have wildcards, since we would have to do some complex name checking to ensure that any changed attributes wouldn’t conflict with elements. We also won’t change any groups that use #all or #choice as a compositor since this could change the meaning of the group in ways we can’t easily predict.

First, we need to find all of the complexTypes in the schema, since those are the only places that attributes can actually be used. We don’t need to search for all attribute declarations, because we can always ask each complexType later what attributes it actually uses. Querying our schema document is a very simple process: iterate through the contents and look for complex types. Note that there are a wide variety of querying methods for examining schema contents, this is just one way to do it.

// Find type definitions: for our purposes, the simplest //  way to get all complexTypes is to drop down to the //  underlying EMF model of a schema to iterate through //  all concrete components contained within this schemaList complexTypeDefinitions = new ArrayList();for (Iterator iter = schema.eAllContents(); iter.hasNext(); ){    XSDConcreteComponent concreteComponent = (XSDConcreteComponent)iter.next();    if (concreteComponent instanceof XSDComplexTypeDefinition)    {        complexTypeDefinitions.add(concreteComponent);    }}// An alternate method would be to use the abstract Infoset //  relationship of schema.getTypeDefinitions(), which would //  get all globally-visible typedefs (simple and complex) //  within the whole schema, however that would miss any //  types that were nested inside of other components

Now that we’ve got a list of all the complexTypes we need to update, let’s exclude any types that we have decided are incompatible with our sample program. Since we are just querying information about various schema components, we can make effective use of the many abstract Infoset relationships and methods that the Model exposes. These abstract methods automatically take into account things like base types and derivation types; references to declarations elsewhere; and effects of imported, included, or redefined schema documents.

// Detect name collisions between top-level elems and attrsList elementNames = getElementNames(complexType);List attributeNames = getAttributeNames(complexType);attributeNames.retainAll(elementNames); if (!attributeNames.isEmpty()) {    // Report the name collision and return...}// Now check for any attribute wildcards, which we //  can't really change into elementsXSDWildcard attributeWildcard = complexType.getAttributeWildcard();if (null != attributeWildcard) {    // Report an incompatible wildcard and return...}// Check the content for other incompatible conditions like //  groups with choice or all or a simpleTypeXSDComplexTypeContent complexTypeContent = complexType.getContent();if (complexTypeContent instanceof XSDSimpleTypeDefinition) {    // Report a simple type as incompatible and return...}else if (null != complexTypeContent){    XSDTerm particleTerm = ((XSDParticle)complexTypeContent).getTerm();    if (particleTerm instanceof XSDModelGroup)    {        XSDCompositor compositor = ((XSDModelGroup)particleTerm).getCompositor();        if ((XSDCompositor.ALL_LITERAL == compositor)                || (XSDCompositor.CHOICE_LITERAL == compositor)) {            // Report an incompatible group type and return...        }    }    // more checks for wildcards, etc.}
Author’s Note: Not all code for detecting incompatibilities is shown here. Please download the samples zip file to see the full story! The MakeSoapCompatible.java program is carefully designed and thoroughly commented to showcase how to manipulate schemas with the Model, and should be your next step if you want to learn more.

Creating Element Declarations
Once we’ve found some complexTypes that we want to update, we need to get concrete. For each complexType we’ll iterate over the getAttributeContents() list of concrete attribute uses that the type has. For each attribute use we first ensure that we’re pointing at the real declaration of the attribute?even if it’s a reference to a declaration elsewhere. Then we want to create an elementDeclaration that has the same name and type as each attribute use?this turns out to be quite straightforward to do. We’ll also put the elementDeclaration inside a new particle’s getContents(), since we will want to add this particle to our complexType later.

if (attrDecl.isAttributeDeclarationReference())    attrDecl = attrDecl.getResolvedAttributeDeclaration();// Create a blank element and simply copy over the //  pertient data about the attributeXSDElementDeclaration elemDecl = XSDFactory.eINSTANCE.createXSDElementDeclaration();elemDecl.setName(attrDecl.getName());elemDecl.setTypeDefinition(attrType);// Note that since an annotation's elements are only modeled //  in the concrete tree that we must explicitly ask to clone themif (null != attrDecl.getAnnotation()) {    cloneAnnotation(attrDecl, elemDecl);}// Wrap this element in a particleXSDParticle particle = XSDFactory.eINSTANCE.createXSDParticle();particle.setContent(elemDecl);

This is an area that clearly shows the difference between the concrete model and the abstract model. You may be wondering what a ‘particle’ is if you’re looking at your schemaDocument.xsd files, since you probably don’t see any xsd:particle elements. You can read the specification for a particle, although it’s quite detailed. A particle is essentially the abstract container of an element declaration, model group, or any (wildcard); the particle is what defines it’s min/maxOccurs constraints at that particular place in the schema. Since the Model can express both the concrete and abstract representations of a schema, it’s easy to work with either kind of representation.

Annotations are the one kind of schema component that are only modeled in the concrete representation of the model, so they require slightly special handling. In this code sample, we will copy over any annotations from the attribute declaration into the new element declaration we just created. We use the DOM cloneNode() method to actually clone or copy over the contents of the annotation component, and then add the annotation itself to the new element declaration.

XSDAnnotation oldAnnotation = attrDecl.getAnnotation();XSDAnnotation newAnnotation = XSDFactory.eINSTANCE.createXSDAnnotation();try {    Element oldAnnElem = oldAnnotation.getElement();     // Use the DOM method to do a deep clone of the element    Element newAnnElem = (Element)oldAnnElem.cloneNode(true);    newAnnotation.setElement(newAnnElem);    elemDecl.setAnnotation(newAnnotation);} catch (Exception e) {    // Report the error and return}

Now that we’ve got a new element declaration to replace our attribute use, we need to swap the two in our complexType component. Since we were careful to use the concrete containment relationship of complexType.getAttributeContents() in our loop, we can simply go add the new elementDeclaration, and then call attrContentsIter.remove() to remove the actual attribute use from our type.

// Use this concrete relationship, since we're going to //  actually remove the attributes from this typefor (ListIterator iter =         complexType.getAttributeContents().listIterator();         iter.hasNext(); /* no-op */ ) {    if (changeAttributeIntoElement(complexType,             (XSDAttributeGroupContent)iter.next(), changedAttrs)) {        // Note that list manipulation calls like remove()         //  will only work properly on concrete lists;         //  attempting to manipulate 'abstract' lists will         //  either throw an exception or will silently fail        iter.remove();    }    else {        // Report the error and continue...    }

Writing Out the Schema
The MakeSoapCompatible sample program will print its status to System.out as it executes. If we detect that there are no attributes actively used in the schema, we report that no changes were made, and exit. Otherwise, we write out a modified schema document to a new name, and report our status: either a list of attributes successfully changed into elements, or a warning about which attributes had name conflicts, and were left unchanged.

Presuming that we’ve changed at least some attribute declarations into equivalent element declarations, we want to save the schema for later use with your SOAP application. The EMF framework that the Model is built on top of provides Resource handling services for loading and saving schema documents in a variety of ways. This code sample shows one very simple way to serialize directly to a URI, in this case an output file on disk named after our original input file.

File outFile = new File(newLocation);FileOutputStream fos = new FileOutputStream(outFile);// Ensure that the abstract model is synchronized with the //  concrete tree: this will ensure that the Model has //  updated the concrete Element in the schema document //  with any changes that may have been made in the //  abstract modelschema.updateElement();// Simply ask the XSDResourceImpl to serialize the schema to //  a document for us; this is just one way we can easily use //  the XSD/EMF framework to manage resources for usXSDResourceImpl.serialize(fos, schema.getElement());fos.close();

As we’ve seen, performing a conceptually simple editing operation on schema documents (turning attributes into elements) can entail a fair amount of work. However the power of the Schema Infoset Model’s representation of both the abstract Infoset of a schema and its concrete representation of the schema documents make this a manageable task. The Model also includes simple tools for loading and saving schema documents to a variety of sources, making it a complete solution for managing your schema repository programmatically.

Some users might ask, “Why not use XSLT or another XML-aware application to edit schema documents?” While XSLT can easily process the concrete model of a set of schema documents, it can’t easily see any of the abstract relationships within the overall schema that they represent. For example, suppose that you need to update any enumerated simpleTypes to include a new ‘UNK‘ enumeration value meaning unknown. Of course, you only want to update enumerations that fit this format of using strings of length 3; you would not want to update numeric or other enumerations.

While XSLT could find all of the simpleType declarations, it cannot understand the relationship between types and base types, or easily evaluate the meanings of facets on those types. The Model’s representation of the abstract Infoset relationships within a schema includes things like simpleType.getEffectiveEnumerationFacets() which takes into account base types, references, and other relationships within the schema. It will return the complete list of enumerations on that simpleType that you can easily query and update with your new value, if appropriate. The Model also includes powerful support for managing namespaces and resolving other types at any point within your schema that would be very difficult to do with other tools.

About the Sample Code
The sample program, MakeSoapCompatible.java, shows the example in this article; anyone interested in exploring further should definitely read the comments there. A simple schema document MakeSoapCompatible.xsd is also included to show a basic purchase order with attributes to be changed into elements. The sample program can also be run against any other schema documents you have. IT requires at least the XSD Schema Infoset Model and the EMF Eclipse Modeling Framework to run standalone.

You can download the sample program and the following utility programs in the sample zip file.

Copies of two other utility .java files normally shipped with the Schema Infoset Model (version 1.0.1 and later) are also attached with commented code showing several other useful techniques. These include:

  • XSDSchemaQueryTools.java showcases a number of other ways to perform advanced queries on schema components.
  • XSDSchemaBuildingTools.java has convenience methods for building schemas programmatically.
  • devxblackblue

    About Our Editorial Process

    At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

    See our full editorial policy.

    About Our Journalist