# Azure XML shared library for Java
[](https://azure.github.io/azure-sdk-for-java)
Azure XML provides shared primitives, abstractions, and helpers for XML.
## Getting started
### Prerequisites
- A [Java Development Kit (JDK)][jdk_link], version 8 or later.
### Include the package
#### Include direct dependency
If you want to take dependency on a particular version of the library that is not present in the BOM,
add the direct dependency to your project as follows.
[//]: # ({x-version-update-start;com.azure:azure-xml;current})
```xml
com.azure
azure-xml
1.2.1
```
[//]: # ({x-version-update-end})
## Key concepts
### XmlSerializable
`XmlSerializable` is used to define how an object is XML serialized and deserialized using stream-style serialization
where the object itself manages the logic for how it's handled with XML. The interface provides an instance-based
`toXml` API which handles writing the object to an `XmlWriter` and a static `fromXml` API which implementations must
override to define how an object is created by reading from an `XmlReader`.
### XmlToken
`XmlToken` is a basic enum that indicates the current state in an XML stream.
### XmlReader
`XmlReader` provides both basic, reading primitive and boxed primitive types, and convenience, reading an object, APIs
for reading XML. `XmlReader` is provided to allow for any underlying XML parser to implement it, such as Woodstox or
XMLStreamReader, as long as the implementation passes the tests provided by this package's test-jar
(`XmlReaderContractTests`).
`XmlReader` is a simplified XML parser where it only supports reading element-by-element with the ability to retrieve
the namespace and attributes for that element. `XmlReader` doesn't progress forward in the XML stream until
`nextElement` is called, meaning that `XmlReader.getIntElement` could be called indefinitely returning the same integer
without error until `nextElement` progresses the XML stream forward.
`XmlReader` doesn't take ownership of the XML input source and therefore won't close any resources if the XML is
provided using an `InputStream` or `Reader`.
### XmlWriter
`XmlWriter` provides basic APIs for writing XML. `XmlWriter` is provided to allow for any underlying XML writer to
implement it, such as Woodstox or XMLStreamWriter, as long as the implementation passes the tests provided by this
package's test-jar (`XmlWriterContractTests`).
`XmlWriter` must be periodically flushed to ensure content written to it is flushed to the underlying container type,
generally an `OutputStream` or `Writer`. Failing to flush may result in content being lost. Closing the `XmlWriter` will
also flush content, so it's best practice to use `XmlWriter` in a try-with-resources block where the `XmlWriter` will
be closed once it's finished being used.
`XmlWriter` doesn't take ownership of the XML output source and therefore won't close any resources if the XML is being
written to an `OutputStream` or `Writer`.
### XmlProvider
`XmlProvider` is a service provider interface which allows for `XmlReader`s and `XmlWriter`s to be created using
implementations found on the classpath. `XmlProvider` can also create the default implementations which are provided by
this package if an implementation isn't found on the classpath.
## Examples
### XmlSerializable
```java xmlserializablesample-basic
public class XmlSerializableExample implements XmlSerializable {
private boolean aBooleanAttribute;
private Double aNullableDecimalAttribute;
private int anIntElement;
private String aStringElement;
@Override
public XmlWriter toXml(XmlWriter xmlWriter) throws XMLStreamException {
return toXml(xmlWriter, null);
}
public XmlWriter toXml(XmlWriter xmlWriter, String rootElementName) throws XMLStreamException {
// If the passed root element name is null or empty use the default root element name.
// This allows for scenarios where the model is defined with one XML root element name but other models use
// it with a separate XML element name.
rootElementName = (rootElementName == null || rootElementName.isEmpty()) ? "example" : rootElementName;
xmlWriter.writeStartElement(rootElementName);
// Writing attributes must happen first so that they are written to the object start element.
xmlWriter.writeBooleanAttribute("aBooleanAttribute", aBooleanAttribute);
xmlWriter.writeNumberAttribute("aNullableDecimalAttribute", aNullableDecimalAttribute);
xmlWriter.writeIntElement("anIntElement", anIntElement);
xmlWriter.writeStringElement("aStringElement", aStringElement);
return xmlWriter.writeEndElement();
}
public XmlSerializableExample fromXml(XmlReader xmlReader) throws XMLStreamException {
return fromXml(xmlReader, null);
}
public XmlSerializableExample fromXml(XmlReader xmlReader, String rootElementName) throws XMLStreamException {
rootElementName = (rootElementName == null || rootElementName.isEmpty()) ? "example" : rootElementName;
// readObject is a convenience method on XmlReader which prepares the XML for being read as an object.
// If the current token isn't an XmlToken.START_ELEMENT the next token element will be iterated to, if it's
// still not an XmlToken.START_ELEMENT after iterating to the next element an exception will be thrown. If
// the next element is an XmlToken.START_ELEMENT it will validate that the XML element matches the name
// expected, if the name doesn't match an exception will be thrown. If the element name matches the reader
// function will be called.
return xmlReader.readObject(rootElementName, reader -> {
// Since this class has no constructor reading to fields can be done inline.
// If the class had a constructor with arguments the recommendation is using local variables to track
// all field values.
XmlSerializableExample result = new XmlSerializableExample();
// Reading attributes must happen first so that the XmlReader is looking at the object start element.
result.aBooleanAttribute = reader.getBooleanAttribute(null, "aBooleanAttribute");
result.aNullableDecimalAttribute = reader.getNullableAttribute(null, "aNullableDecimalAttribute",
Double::parseDouble);
while (reader.nextElement() != XmlToken.END_ELEMENT) {
QName elementName = reader.getElementName();
// Since this object doesn't use namespaces we can work with the local part directly.
// If it had namespaces the full QName would need to be inspected.
String localPart = elementName.getLocalPart();
if ("anIntElement".equals(localPart)) {
result.anIntElement = reader.getIntElement();
} else if ("aStringElement".equals(localPart)) {
// getStringElement coalesces XML text and XML CData into a single string without needing to
// manage state.
result.aStringElement = reader.getStringElement();
} else {
// Skip element when the element is unknown.
reader.skipElement();
}
}
return result;
});
}
}
```
## Next steps
Get started with Azure libraries that are [built using Azure Core](https://azure.github.io/azure-sdk/releases/latest/#java).
## Troubleshooting
If you encounter any bugs, please file issues via [GitHub Issues](https://github.com/Azure/azure-sdk-for-java/issues/new/choose)
or checkout [StackOverflow for Azure Java SDK](https://stackoverflow.com/questions/tagged/azure-java-sdk).
### Android Support
While the Azure SDKs for Java do not explicitly support Android, `azure-xml` is capable of being used in an Android
application. However, for `azure-xml` to work on Android the Android application needs to add a dependency on
[javax.xml.stream:stax-api](https://central.sonatype.com/artifact/javax.xml.stream/stax-api) which provides
`javax.xml.stream` to environments that don't have them (such as Android).
## Contributing
For details on contributing to this repository, see the [contributing guide](https://github.com/Azure/azure-sdk-for-java/blob/main/CONTRIBUTING.md).
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
[jdk_link]: https://learn.microsoft.com/java/azure/jdk/?view=azure-java-stable