问题
A noobie question. I am generating Java code from a dtd file (USPTO dtd). The DTD specifies the root element as follows:
<!ELEMENT us-patent-grant (doc-page+ | (us-bibliographic-data-grant , abstract* , drawings? , description , us-sequence-list-doc? , us-megatable-doc?,table-external-doc* , us-chemistry* , us-math* ,us-claim-statement , claims))>
When I run xjc with the following binding schema
<?xml version="1.0"?>
<xml-java-binding-schema version="1.0ea2">
<element name="us-patent-grant" type="class" root="true"></element>
</xml-java-binding-schema>
I see the following Java object generated
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims"
})
@XmlRootElement(name = "us-patent-grant")
public class UsPatentGrant {
....
@XmlElements({
@XmlElement(name = "doc-page", required = true, type = DocPage.class),
@XmlElement(name = "us-bibliographic-data-grant", required = true, type = UsBibliographicDataGrant.class),
@XmlElement(name = "abstract", required = true, type = Abstract.class),
@XmlElement(name = "drawings", required = true, type = Drawings.class),
@XmlElement(name = "description", required = true, type = Description.class),
@XmlElement(name = "us-sequence-list-doc", required = true, type = UsSequenceListDoc.class),
@XmlElement(name = "us-megatable-doc", required = true, type = UsMegatableDoc.class),
@XmlElement(name = "table-external-doc", required = true, type = TableExternalDoc.class),
@XmlElement(name = "us-chemistry", required = true, type = UsChemistry.class),
@XmlElement(name = "us-math", required = true, type = UsMath.class),
@XmlElement(name = "us-claim-statement", required = true, type = UsClaimStatement.class),
@XmlElement(name = "claims", required = true, type = Claims.class)
})
protected List<Object> docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims;
.........
public List<Object> getDocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims() {
if (docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims == null) {
docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims = new ArrayList<Object>();
}
return this.docPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims;
}
So my question is how can i change the name of the long getter
getDocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims
in the binding schema?
thank you in advance.
回答1:
This isn't so much an answer as evidence for what you are suspecting.
In the JAXB/XJC source for com.sun.tools.xjc.reader.dtd.Element
, in the bind() method, you can see this excerpt, iterating through the child elements
of the Element
:
if(b.isRepeated || b.elements.size()>1) {
// collection
StringBuilder name = new StringBuilder();
for( Element e : b.elements ) {
if(name.length()>0)
name.append("Or");
name.append(owner.model.getNameConverter().toPropertyName(e.name));
}
...
} else {
// single property
String name = b.elements.iterator().next().name;
String propName = owner.model.getNameConverter().toPropertyName(name);
...
}
That looks like it offers little opportunity to change the sequence of "Or"
separators where there are multiple child elements.
You can see the bindInfo
allowing custom names elsewhere in Element
, but not near here. Unless someone else can spot it, the chances to rename things seem to end there. In short, the CElementPropertyInfo
this outputs leads to BeanGenerator.fields
, which lead to JDefinedClass.fields
which are then output directly by JDefinedClass.declareBody()
.
As the XJC docs say, DTD support is still experimental...
回答2:
As @df778899 mentioned, if you looked at the below source code then nothing much can be done about it
CElementPropertyInfo p;
if(b.isRepeated || b.elements.size()>1) {
// collection
StringBuilder name = new StringBuilder();
for( Element e : b.elements ) {
if(name.length()>0)
name.append("Or");
name.append(owner.model.getNameConverter().toPropertyName(e.name));
}
p = new CElementPropertyInfo(name.toString(), REPEATED_ELEMENT, ID.NONE, null, null,null/*TODO*/, locator, !b.isOptional );
for( Element e : b.elements ) {
CClassInfo child = owner.getOrCreateElement(e.name).getClassInfo();
assert child!=null; // we are requiring them to be classes.
p.getTypes().add(new CTypeRef(child,new QName("",e.name),null,false,null));
}
The CElementPropertyInfo
object takes the name and the only way to fix is to use instrumentation. So I created a maven project
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.tarunlalwani</groupId>
<artifactId>jxc-customizer</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/javassist/javassist -->
<!-- https://mvnrepository.com/artifact/org.javassist/javassist -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>com.xjc.javaagent.SimpleAgent</Premain-Class>
<Boot-Class-Path>libs/javassist-3.22.0-GA.jar</Boot-Class-Path>
<Class-Path>libs/javassist-3.22.0-GA.jar</Class-Path>
</manifestEntries>
<manifest>
<classpathPrefix>libs/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
jxccustomizer/src/main/java/com/xjc/javaagent/SimpleAgent.java
package com.xjc.javaagent;
import java.lang.instrument.Instrumentation;
public class SimpleAgent {
public static void premain(String agentArgs,
Instrumentation instrumentation){
System.out.println("Starting Agent");
SimpleClassFileTransformer transformer =
new SimpleClassFileTransformer();
instrumentation.addTransformer(transformer);
}
}
jxccustomizer/src/main/java/com/xjc/javaagent/SimpleClassFileTransformer.java
package com.xjc.javaagent;
import javassist.*;
import java.io.ByteArrayInputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class SimpleClassFileTransformer implements
ClassFileTransformer {
public byte[] transform(ClassLoader loader,
String className, Class classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer)
throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
if (className.endsWith("/CPropertyInfo")) {
System.out.println("Loading class - " + className);
try {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(
new ByteArrayInputStream(classfileBuffer));
CtConstructor[] constructors = ctClass.getConstructors();
for (CtConstructor c : constructors) {
c.insertBefore("{ String propFile = System.getProperty(\"XJC_REMAP\");\n" +
" if (propFile != null)\n" +
" {\n" +
" System.out.println(\"External remap file provided\");\n" +
" java.util.Properties props = new java.util.Properties();\n" +
" try {\n" +
" props.load(new java.io.FileInputStream(propFile));\n" +
" java.util.Enumeration enums = props.propertyNames();\n" +
" while (enums.hasMoreElements()) {\n" +
" String key = (String)enums.nextElement();\n" +
" String value = props.getProperty(key);\n" +
" try {\n" +
" System.out.println(\"Checking if \" + name + \" matches \" + key);\n" +
" java.util.regex.Pattern pat = java.util.regex.Pattern.compile(\"^\" + key +\"$\", java.util.regex.Pattern.CASE_INSENSITIVE);\n" +
" if (pat.matcher(name).find())\n" +
" {\n" +
" System.out.println(\"Replacing \" + name + \" with \" + value);\n" +
" name = value;\n" +
" break;\n" +
" }\n" +
" } finally {\n" +
" if (name == key) {\n" +
" System.out.println(\"Replacing \" + name + \" with \" + value);\n" +
" name = value;\n" +
" }\n" +
" break;\n" +
" }\n" +
" }\n" +
" } catch (java.io.IOException e) {\n" +
" e.printStackTrace();\n" +
" }\n" +
" }}");
}
byteCode = ctClass.toBytecode();
ctClass.detach();
System.out.println("NO Exception occured");
} catch (Throwable e) {
System.out.println("Exception occurred");
e.printStackTrace();
}
}
return byteCode;
}
}
The above class can load a properties file specified by -DXJC_REMAP=<filepath>
which will be of below format
remap.properties
docPageOr.*=patentDocument
Now to test the solution. First need to generated the jar
for the agent by running below command
mvn clean package
Next is to create some testing dtd
binding.xjb
<?xml version="1.0"?>
<xml-java-binding-schema>
<options package="org"/>
<element name="us-patent-grant" type="class" root="true"></element>
</xml-java-binding-schema>
testing.dtd
<?xml version="1.0" encoding="utf-8"?>
<!-- DTD to write simple stories
Made by Daniel K. Schneider / TECFA / University of Geneva
VERSION 1.0
30/10/2003 -->
<!ELEMENT us-patent-grant (doc-page+ | (us-bibliographic-data-grant , abstract* , drawings? , description , us-sequence-list-doc? , us-megatable-doc?,table-external-doc* , us-chemistry* , us-math* ,us-claim-statement , claims))>
Now to use this to run the agent with xjc
$ export _JAVA_OPTIONS="-javaagent:/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/jxccustomizer/target/jxc-customizer-1.0-SNAPSHOT.jar -DXJC_REMAP=/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/remap.properties"
$ xjc -b binding.xjb -dtd testing.dtd
Picked up _JAVA_OPTIONS: -javaagent:/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/jxccustomizer/target/jxc-customizer-1.0-SNAPSHOT.jar -DXJC_REMAP=/Users/tarun.lalwani/Desktop/tarunlalwani.com/tarunlalwani/workshop/ub16/so/jaxb/remap.properties
objc[33035]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/xjc (0x10ed964c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10eded4e0). One of the two will be used. Which one is undefined.
Starting Agent
Loading class 2 - com/sun/tools/internal/xjc/model/CPropertyInfo
inside try
Updating cons 1
Updating cons
NO Exception occured
parsing a schema...
External remap file provided
Checking if DocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims matches docPageOr.*
Replacing DocPageOrUsBibliographicDataGrantOrAbstractOrDrawingsOrDescriptionOrUsSequenceListDocOrUsMegatableDocOrTableExternalDocOrUsChemistryOrUsMathOrUsClaimStatementOrClaims with patentDocument
compiling a schema...
org/Abstract.java
org/Claims.java
org/Description.java
org/DocPage.java
org/Drawings.java
org/ObjectFactory.java
org/TableExternalDoc.java
org/UsBibliographicDataGrant.java
org/UsChemistry.java
org/UsClaimStatement.java
org/UsMath.java
org/UsMegatableDoc.java
org/UsPatentGrant.java
org/UsSequenceListDoc.java
And now the content of generated file is as desired
The git repo for the above code is available on below link
https://github.com/tarunlalwani/xjc-java-agent-customization
来源:https://stackoverflow.com/questions/18667358/jaxb-binding-schema-of-dtd-file