根据xsd定义的xml格式封装数据并生成xml文件
在项目组最近一次的版本开发中,有个需求是将我们项目数据库中的基础数据生成到xml文件中,并通知第三方获取这个xml文件。刚开始是想将数据按照xml的格式拼接成字符串,并逐行的写到文件中,但是总觉得这样做和java的面向对象格格不入。于是baidu了一下,果然有比拼接字符串更好的办法,具体实现如下:
1、将要生成的xml文件的格式编写到xsd文件中,如我们项目中用到的Channel.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wmh="http://www.wmhelp.com/2003/eGenerator" elementFormDefault="qualified">
<xs:element name="ADI">
<xs:complexType>
<xs:sequence>
<xs:element ref="CategoryList"/>
<xs:element ref="ChannelList"/>
<xs:element ref="Mappings"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CategoryList">
<xs:complexType>
<xs:sequence>
<xs:element ref="Objects" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Objects">
<xs:complexType>
<xs:sequence>
<xs:element ref="Object" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Object">
<xs:complexType>
<xs:sequence>
<xs:element ref="Property" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="ElementType" type="xs:string" use="required"/>
<xs:attribute name="ObjectID" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="Property">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="Name" type="xs:string" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="ChannelList">
<xs:complexType>
<xs:sequence>
<xs:element ref="Channel" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Channel">
<xs:complexType>
<xs:sequence>
<xs:element ref="Objects"/>
<xs:element ref="Mappings"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Mappings">
<xs:complexType>
<xs:sequence>
<xs:element ref="Mapping" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Mapping">
<xs:complexType>
<xs:sequence>
<xs:element ref="Property"/>
</xs:sequence>
<xs:attribute name="ParentType" type="xs:string" use="required"/>
<xs:attribute name="ParentID" type="xs:string" use="required"/>
<xs:attribute name="ParentCode" type="xs:string" use="required"/>
<xs:attribute name="ElementType" type="xs:string" use="required"/>
<xs:attribute name="ElementID" type="xs:string" use="required"/>
<xs:attribute name="ElementCode" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
2、借助于jdk的xjc命令,将xml中的节点转换成java对象类以供数据封装,具体实现是编写一个bat文件,直接执行bat文件即可,bat文件中的命令如下:
xjc -d "D:\Lcug30Code\1.7.08_EPG\src" -p "com.xxx.xxx.epg.xml.bean.channel" "D:\Lcug30Code\aa\Channel.xsd",
这样就会在com.xxx.xxx.epg.xml.bean.channel包下生成节点对象类ADI.java、CategoryList.java等和ObjectFactory(创建对象的工厂类)
3、准备数据封装,将从数据库中查询出来的数据按照xml格式从底层节点开始,逐层向上封装,如封装CategoryList节点:
/**
* 将频道栏目信息封装成CategoryList节点
*
* @return CategoryList
* @see [类、类#方法、类#成员]
*/
private CategoryList createCategoryList()
{
Map<String, TypeInfoRecord> map = getNDSIPTVSubjectData();
CategoryList category = factory.createCategoryList();
List<Objects> list = category.getObjects();
Objects objects = null;
ObjectNode object = null;
TypeInfoRecord[] typeInfos = map.values()
.toArray(new TypeInfoRecord[map.size()]);
List<ObjectNode> objectList = null;
List<Property> propertyList = null;
Map<String, String> typeDomain = null;
for (int i = 0; i < typeInfos.length; i++)
{
objects = factory.createObjects();
objectList = objects.getObject();
object = factory.createObject();
propertyList = object.getProperty();
object.setElementType("Category");
object.setObjectID("Category@" + typeInfos[i].getTypeId());
propertyList.add(getProperty("ContentID", typeInfos[i].getTypeId()));
propertyList.add(getProperty("Name", typeInfos[i].getTypeNamelang1()));
propertyList.add(getProperty("Name_fg", typeInfos[i].getTypeNamelang2()));
propertyList.add(getProperty("ParentID", typeInfos[i].getParentTypeId()));
propertyList.add(getProperty("Sequence", String.valueOf(typeInfos[i].getDisplayIndex())));
propertyList.add(getProperty("Description", typeInfos[i].getTypeIntrolang1()));
propertyList.add(getProperty("Description_fg", typeInfos[i].getTypeIntrolang2()));
propertyList.add(getProperty("Display_index", String.valueOf(typeInfos[i].getOrderType())));
propertyList.add(getProperty("Service_type", String.valueOf(typeInfos[i].getServiceConstraint())));
typeDomain = typeInfos[i].getTypeDomains();
String[] typeDomains = typeDomain.keySet()
.toArray(new String[typeDomain.size()]);
propertyList.add(getProperty("Domain", getTypeDomainStr(typeDomains)));
objectList.add(object);
list.add(objects);
}
return category;
}
/**
* 根据Name和Value生成Property节点
*
* @param name
* @param value
* @return Property
* @see [类、类#方法、类#成员]
*/
private Property getProperty(String name, String value)
{
Property property = factory.createProperty();
property.setName(name);
property.setValue(value);
return property;
}
/**
* 生成ADI对象
* @return ADI XML文件的根节点
*/
public ADI getAdiInfo()
{
createXMLRunnable = true;
// 获取XML文件的根对象
ADI adi = factory.createADI();
// 分别设置节点
adi.setCategoryList(createCategoryList());
adi.setChannelList(createChannelList());
adi.setMappings(createMappings());
// 如果标识位为false,那么就返回NULL
if (!createXMLRunnable)
{
return null;
}
return adi;
}
4、使用jdk的JAXBContext将封装好的数据输出到xml文件:
public static void buildXml(java.lang.Object adi, String filePath,
String packageName)
{
FileOutputStream fileOutputStream = null;
File file;
try
{
file = new File(filePath);
fileOutputStream = new FileOutputStream(file);
jaxbContext = JAXBContext.newInstance(packageName);
marshaller = jaxbContext.createMarshaller();
// 设置XML文件的编码格式
marshaller.setProperty("jaxb.encoding", EPGConfig.getInstance()
.getXmlEncode());
// 将XML文件格式化输出
marshaller.setProperty("jaxb.formatted.output", true);
marshaller.marshal(adi, fileOutputStream);
}
catch (JAXBException e)
{
EPGSysLogger.error(EPGConstants.ALL_AREA,
"Exception in buildXml , the Exception is " + e.toString());
createXMLRunnable = false;
}
catch (FileNotFoundException e)
{
EPGSysLogger.error(EPGConstants.ALL_AREA,
"Exception in buildXml , the Exception is " + e.toString());
createXMLRunnable = false;
}
finally
{
// 关流
if (null != fileOutputStream)
{
try
{
fileOutputStream.close();
}
catch (IOException e)
{
EPGSysLogger.error(EPGConstants.ALL_AREA,
"Exception in buildXml , the Exception is "
+ e.toString());
createXMLRunnable = false;
}
}
}
}
其中参数adi就是第3步的getAdiInfo封装的ADI对象,filePath是文件名(包含完整的绝对路径),packageName就是xml节点对应的java对象所在的包名(见步骤2),
这样一个完整的满足换行和缩进的xml文件就生成了。