在JAXB输出中,我正在尝试填充xsi:schemaLocation属性,因此它包含所有所使用的命名空间和模式的完整信息。我试图找到比硬编码更好的方法!
我们有一个XML模式,分布在大约50个文件中。每个文件都定义一个类型并具有自己的命名空间。 (我认为这不是一个聪明的决定,但是为时过晚而无法争辩。)XJC很乐意编写整个混乱并生成课程。当输出被编组时,所有50个以上的命名空间都被正确地放在根元素上。
问题是我被要求用所有命名空间和关联的模式源填充xsi:schemaLocation属性。因此,我需要在其中放置50多对,而不是仅仅一个命名空间-URL对(“my.co.schema.v1 http://my.company.com/schemas/my.co.schema.v1.xsd”)。当然我可以硬编码,但这是错误的。
我想查询JAXB关于它使用的所有来源,但这似乎不可能。我开始认为我必须使用Xerces语法池并再次读取所有模式。这是最好的选择吗?
我在下面添加了一个SCCE。工作正常,但正如我所提到的,xsi:schemaLocation属性是硬编码且不完整的。
请提前帮助,谢谢。
package my.xml.generator;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import my.schema.ComponentType;
import my.schema.ObjectFactory;
import my.schema.ProductType;
/**
* Builds a JAXB object and serializes to a file.
*
* Many thanks to
* http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html
*/
public class SimpleXmlBuilder {
public static void main(String[] argv) throws Exception {
if (argv.length != 1)
throw new IllegalArgumentException(
"Usage: SimpleXmlBuilder output-file-path");
// Create a trivial component
ObjectFactory objectFactory = new ObjectFactory();
ProductType product = objectFactory.createProductType();
product.setDeprecated(true);
// Wrap in a root element
JAXBElement<ProductType> rootElement = objectFactory
.createProduct(product);
// Get JAXB going
Class<?>[] rootElementClass = { ComponentType.class };
JAXBContext context = JAXBContext.newInstance(rootElementClass);
Marshaller marshaller = context.createMarshaller();
// Leave some whitespace for humans
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// The critical part of the problem!
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"My.Name.Space.v1" + " "
+ "http://my.server.com/schemas/My.Name.Space.v1.xsd");
// Write to the named file
marshaller.marshal(rootElement, new File(argv[0]));
System.out.println("Wrote generated XML to " + argv[0]);
}
}
答案 0 :(得分:1)
您可以利用javax.xml.validation
API通过LSResourceResolver
解析所有架构位置。
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.w3c.dom.ls.*;
public class Demo {
public static void main(String[] args) throws Exception {
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
MyLSResourceResolver resourceResolver = new MyLSResourceResolver();
sf.setResourceResolver(resourceResolver);
StreamSource rootSchemaSource = new StreamSource("src/forum17992435/schema1.xsd");
sf.setResourceResolver(resourceResolver);
Schema schema = sf.newSchema(rootSchemaSource);
}
private static class MyLSResourceResolver implements LSResourceResolver {
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
System.out.print(namespaceURI);
System.out.print(" - ");
System.out.println(systemId);
return null;
}
}
}
<强> schema1.xsd 强>
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema1" xmlns:tns="http://www.example.org/schema1" elementFormDefault="qualified">
<import namespace="http://www.example.org/schema2" schemaLocation="schema2.xsd"/>
</schema>
<强> schema2.xsd 强>
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema2" xmlns:tns="http://www.example.org/schema2" elementFormDefault="qualified">
<import namespace="http://www.example.org/schema3" schemaLocation="schema3.xsd"/>
</schema>
<强> schema3.xsd 强>
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/schema3" xmlns:tns="http://www.example.org/schema3" elementFormDefault="qualified">
</schema>
该演示将输出2个导入模式的位置。您需要提供第一个XML模式的位置。
http://www.example.org/schema2 - schema2.xsd
http://www.example.org/schema3 - schema3.xsd
答案 1 :(得分:0)
非常感谢Blaise的指针!他的解决方案绝对有效,它确认我必须再次从所有模式中读取工作,从根模式开始。任何地方的JAXB工件都无法获得该信息。
示例代码如下,资源解析器收集架构工厂找到的URI和关联的系统ID对。解析程序还使用真正的XML目录文件,因此架构处理器使用本地缓存的架构文件(而不是通过网络读取URL)。还添加了使用新加载的模式验证内存中JAXB对象的代码。
下面的解决方案仅需要Java 1.6,不需要第三方库。我担心使用一个标有“内部”的课程。
package my.xml.generator;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.util.JAXBSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.ls.LSInput;
import my.schema.ObjectFactory;
import my.schema.ProductType;
import com.sun.org.apache.xerces.internal.util.XMLCatalogResolver;
/**
* Builds a JAXB object, validates it then serializes it to a file.
*
* Credit to Blaise Doughan for answering questions and these posts:
* http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html
* http://blog.bdoughan.com/2010/11/validate-jaxb-object-model-with-xml.html
*
* Also helpful:
* http://stackoverflow.com/questions/7364840/xmlschema-validation-with-
* catalog-xml-file-for-entity-resolving
*/
public class ValidatingXmlBuilder {
/**
* Hooks in to the system resolver to gather details about schemas.
*/
static class MyXMLCatalogResolver extends XMLCatalogResolver {
/** List of "URI - SystemID" strings */
private final List<String> namespaceUrlPairs;
public MyXMLCatalogResolver(String[] catalogs) {
super(catalogs);
namespaceUrlPairs = new ArrayList<String>();
}
/**
* Gathers namespace URI and system ID pairs
*/
@Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
String pair = namespaceURI + " " + systemId;
namespaceUrlPairs.add(pair);
// System.out.println("Resolver found URI-system ID: " + pair);
return super.resolveResource(type, namespaceURI, publicId,
systemId, baseURI);
}
/**
* @return String containing pairs of namespace URI and corresponding
* system ID, using a single space as separators
*/
public String getSchemaLocationPairs() {
StringBuffer sb = new StringBuffer();
for (String pair : namespaceUrlPairs)
sb.append(pair + " ");
return sb.toString().trim();
}
}
public static void main(String[] argv) throws Exception {
if (argv.length != 3)
throw new IllegalArgumentException(
"Usage: ValidatingXmlBuilder schema-file catalog-file output-file");
// TODO validate existence of files
String schemaPath = argv[0];
String catalogPath = argv[1];
String outputPath = argv[2];
// Create a schema with a catalog resolver to gather details
System.out.println("Reading schema from " + schemaPath);
System.out.println("Reading catalog from " + catalogPath);
MyXMLCatalogResolver hookedResolver = new MyXMLCatalogResolver(
new String[] { catalogPath });
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setResourceResolver(hookedResolver);
// Read in the schema to gather the info
Schema schema = schemaFactory.newSchema(new File(schemaPath));
// Create a trivial product and wrap it in root element
ObjectFactory objectFactory = new ObjectFactory();
ProductType product = objectFactory.createProductType();
product.setSchemaVersion("1.0");
JAXBElement<ProductType> rootElement = objectFactory
.createProduct(product);
// Get JAXB going for a this root element type
Class<?>[] rootElementClass = { ProductType.class };
JAXBContext jaxbContext = JAXBContext.newInstance(rootElementClass);
// Create a validator and check the object
// TODO: Create custom instance of org.xml.sax.ErrorHandler
Validator validator = schema.newValidator();
JAXBSource source = new JAXBSource(jaxbContext, rootElement);
validator.validate(source);
// Create and configure marshaller with gathered info
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// TODO: Fixup URI of root schema, specified here as file
marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
hookedResolver.getSchemaLocationPairs());
// Write to the named file
marshaller.marshal(rootElement, new File(outputPath));
System.out.println("Wrote generated XML to " + outputPath);
}
}