我尝试使用 SAAJ 手动处理 Web 服务请求,但仍根据 Web 服务的 WSDL 架构验证接收到的请求。这个特定的 WSDL 包含多个元素,我使用 wsdl4j 提取这些元素,当我尝试创建一个新的 Validator 时,我收到一条关于 Validator 无法解析一个模式中的引用的错误消息在另一个元素中定义的元素:
org.xml.sax.SAXParseException; src-resolve: Cannot resolve the name 'ns0:CustomerNumber' to a(n) 'element declaration' component.
我已经将实际的 WSDL 和代码简化为一些小的东西来创建可重现的东西。 “simple.wsdl”也在 Soap-UI 中正确加载。这是在带有 jdk1.7.0_51 的 Windows 7 上。
请注意,我注意到此问题的原始 WSDL 是由 TIBCO BusinessWorks 生成的,因此我认为这不是无效 WSDL 的问题。
我在 SO 上看到了其他类似的问题,但提供的解决方案似乎并不完全符合我的场景,所以我想我会再问一次。
有没有人知道发生了什么以及我如何让它工作?
测试代码:
import java.util.ArrayList;
import javax.wsdl.Definition;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.XMLConstants;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.junit.Test;
import org.w3c.dom.Element;
public class TestIt {
@Test
public void testWSDLSchema() throws Exception {
WSDLFactory wsdlFactory = WSDLFactory.newInstance();
WSDLReader reader = wsdlFactory.newWSDLReader();
Definition wsdlDefinition = reader.readWSDL("test/resources/simple.wsdl");
ArrayList<Element> wsdlSchemas = new ArrayList<Element>();
for (Object o : wsdlDefinition.getTypes().getExtensibilityElements()) {
if (o instanceof Schema) {
wsdlSchemas.add(((Schema) o).getElement());
}
}
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
ArrayList<DOMSource> asrcs = new ArrayList<DOMSource>();
for (Element e : wsdlSchemas) {
asrcs.add(new DOMSource(e));
}
DOMSource sources[] = asrcs.toArray(new DOMSource[0]);
javax.xml.validation.Schema schema = factory.newSchema(sources);
Validator schemaValidator = schema.newValidator();
}
}
完整堆栈跟踪:
Retrieving document at 'test/resources/simple.wsdl'.
org.xml.sax.SAXParseException; src-resolve: Cannot resolve the name 'ns0:CustomerNumber' to a(n) 'element declaration' component.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:347)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4166)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4145)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1678)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseLocal(XSDElementTraverser.java:170)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseLocalElements(XSDHandler.java:3618)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:633)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:252)
at TestIt.testWSDLSchema(TestIt.java:37)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:202)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:65)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
WSDL:
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:ns10="http://www.abc.com/ServiceA"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:ns13="http://www.abc.com/Common"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:ns2="http://www.abc.com/DetailsResponse"
xmlns:ns3="http://www.abc.com/ErrorSchema"
xmlns:ns1="http://www.abc.com/DetailsRequest"
xmlns:tns="http://www.abc.com/Service"
name="Untitled"
targetNamespace="http://www.abc.com/Service">
<wsdl:types>
<xs:schema
xmlns="http://www.abc.com/DetailsRequest"
xmlns:ns0="http://www.abc.com/Common"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.abc.com/DetailsRequest"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xs:import namespace="http://www.abc.com/Common"/>
<xs:element name="DetailsRequest">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns1:RequestBody"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="RequestBody">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns0:CustomerNumber"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema
xmlns="http://www.abc.com/DetailsResponse"
xmlns:ns0="http://www.abc.com/Common"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.abc.com/DetailsResponse"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:import namespace="http://www.abc.com/Common"/>
<xs:element name="DetailsResponse">
<xs:complexType>
<xs:sequence>
<xs:element ref="ns0:AccountNumber"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
<xs:schema
xmlns="http://www.abc.com/Common"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.abc.com/Common"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="CustomerNumber">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="AccountNumber">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:schema>
<xs:schema
xmlns="http://www.abc.com/ErrorSchema"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.abc.com/ErrorSchema"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="ErrorSchema">
<xs:complexType>
<xs:sequence>
<xs:element name="ErrorResponseBody" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:service name="ServiceA">
<wsdl:port name="ServiceAEndpoint" binding="tns:ServiceABinding">
<soap:address location="http://localhost:7232/ServiceA/ServiceAEndpoint"/>
</wsdl:port>
</wsdl:service>
<wsdl:portType name="ServiceA">
<wsdl:operation name="GetDetails">
<wsdl:input message="tns:DetailsRequest"/>
<wsdl:output message="tns:DetailsResponse"/>
<wsdl:fault name="fault1" message="tns:DetailsErrorResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ServiceABinding" type="tns:ServiceA">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="GetDetails">
<soap:operation style="document" soapAction="/ServiceA/ServiceAEndpoint/GetDetails"/>
<wsdl:input>
<soap:body use="literal" parts="DetailsRequest"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal" parts="DetailsResponse"/>
</wsdl:output>
<wsdl:fault name="fault1">
<soap:fault use="literal" name="fault1"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:message name="DetailsRequest">
<wsdl:part name="DetailsRequest" element="ns1:DetailsRequest"/>
</wsdl:message>
<wsdl:message name="DetailsResponse">
<wsdl:part name="DetailsResponse" element="ns2:DetailsResponse"/>
</wsdl:message>
<wsdl:message name="DetailsErrorResponse">
<wsdl:part name="DetailsErrorResponse" element="ns3:ErrorSchema"/>
</wsdl:message>
</wsdl:definitions>
最佳答案
我必须同时使用 LSResourceResolver 并将命名空间声明从 wsdl 文档复制到模式元素才能完成这项工作。这是代码:
public static Schema makeSchema(String pathToWsdl)
throws ParserConfigurationException, IOException, SAXException, InstantiationException, IllegalAccessException, ClassNotFoundException {
// read wsdl document
File wsdlFile = new File(pathToWsdl);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document wsdlDoc = dBuilder.parse(wsdlFile);
// read namespace declarations from wsdl document, in case they are referred from a schema
NamedNodeMap attributes = wsdlDoc.getDocumentElement().getAttributes();
Map<String, String> namespacesFromWsdlDocument = new HashMap<>();
for (int i = 0; i < attributes.getLength(); i++) {
Node n = attributes.item(i);
if (n.getNamespaceURI() != null && n.getNamespaceURI().equals("http://www.w3.org/2000/xmlns/")) {
namespacesFromWsdlDocument
.put(n.getLocalName(), n.getNodeValue());
}
}
// read the schema nodes from the wsdl
NodeList schemas = wsdlDoc.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "schema");
Map<String, DOMSource> sources = new HashMap<>();
for (int i = 0; i < schemas.getLength(); i++) {
// create a document for each schema and copy the source schema
Document schema = dBuilder.newDocument();
Element schemaElement = (Element)schema.importNode(schemas.item(i), true);
// add all non-existing namespace declarations from the wsdl node
String targetNs = schemaElement.getAttribute("targetNamespace");
for (Map.Entry<String, String> ns : namespacesFromWsdlDocument.entrySet()) {
String name = ns.getKey();
String value = ns.getValue();
if (schemaElement.getAttributeNodeNS("http://www.w3.org/2000/xmlns/", name) == null) {
schemaElement.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:" + name, value);
}
}
// map schemas by their target namespace
schema.appendChild(schemaElement);
DOMSource domSource = new DOMSource(schema);
sources.put(targetNs, domSource);
}
SchemaFactory factory =
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// Create a ResourceResolver that can find the correct schema from the map
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
final DOMImplementationLS domImplementationLS = (DOMImplementationLS) registry.getDOMImplementation("LS");
factory.setResourceResolver(new LSResourceResolver() {
@Override public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
Source xmlSource = sources.get(namespaceURI);
if (xmlSource != null) {
LSInput input = domImplementationLS.createLSInput();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Result outputTarget = new StreamResult(outputStream);
try {
TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
} catch (TransformerException e) {
e.printStackTrace();
}
InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
input.setByteStream(is);
input.setSystemId(systemId);
return input;
} else {
return null;
}
}
});
// create the schema object from the sources
return factory.newSchema(sources.values().toArray(new DOMSource[]{}));
}
这里也回答了同样的问题: Java, Validate XML against an embedded schema in WSDL
关于java - 针对从 WSDL 中提取的多个模式验证 XML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21238601/