3

JAXB 出力では、使用されたすべての名前空間とスキーマの完全な情報が含まれるように、xsi:schemaLocation 属性を設定しようとしています。ハードコーディングするよりも良い方法を見つけようとしています!

約 50 個のファイルにまたがる XML スキーマがあります。各ファイルは型を定義し、独自の名前空間を持ちます。(賢明な決定ではないと思いますが、それを主張するには遅すぎます。) XJC は混乱全体を喜んでコンパイルし、クラスを生成します。出力がマーシャリングされると、50 以上の名前空間がすべて適切にルート要素に配置されます。

問題は、xsi:schemaLocation 属性にすべての名前空間と関連するスキーマ ソースを入力するように求められたことです。したがって、1 つの名前空間と URL のペア (「my.co.schema.v1 http://my.company.com/schemas/my.co.schema.v1.xsd」) ではなく、50 以上のペアを配置する必要があります。そこで。確かにハードコードできますが、それは間違っています。

JAXB が使用したすべてのソースについて 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]);
  }

}
4

2 に答える 2

0

ポインターを提供してくれたブレイズに感謝します!彼のソリューションは完全に機能し、ルート スキーマから始めて、すべてのスキーマを再度読み込む作業を行う必要があることが確認されました。その情報は、どこの 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);
  }

}
于 2013-08-06T11:08:09.923 に答える