XSD to JAVA using jaxb

@Vampire are you out there??? :slight_smile:

Gradle gurus, I am STUCK. Looking to use org.glassfish.jaxb to convert XSD to Java. I have searched the internet over and came across a great Youtube video https://www.youtube.com/watch?v=tsRDNPWITZA (Thank Luke Chaffeey) then StackOverflow - How to run jaxb xjc task with gradle? - Stack Overflow (Which seems to use the YouTube approach)

While running

 ./gradlew generateSources

I get the following:

Could not find method compile() for arguments [generated classes] on object of type org.gradle.api.internal.artifacts.dsl.dependencies.DefaultDependencyHandler.

// BEGIN jaxb convert XSD to Java Classes
sourceSets {
    generated {
        // java.srcDir "$generated_dir"  // >> is this supposed to be in the gradle.properties asking for 
        // knowledge
        java.srcDir("${projectDir}/src/main/java")
    }
}

// JAXB configuration holds classpath for running the JAXB XJC compiler
configurations {
    jaxb
}

dependencies {
    jaxb "org.glassfish.jaxb:jaxb-xjc:2.4.0-b180830.0438"
    jaxb "org.glassfish.jaxb:jaxb-runtime:2.4.0-b180830.0438"


    implementation "org.glassfish.jaxb:jaxb-xjc:2.4.0-b180830.0438"
    implementation "org.glassfish.jaxb:jaxb-runtime:2.4.0-b180830.0438"
}

dependencies {
    compile sourceSets.generated.output

    // Generated code depends on the JAXB API, which is removed from base Java in JDK 11
    compile "org.glassfish.jaxb:jaxb-runtime:2.4.0-b180830.0438"
    generatedComplile "org.glassfish.jaxb:jaxb-runtime:2.4.0-b180830.0438"
}

// XJC tasks

// Cookie cutter function for defining multiple XJC tasks
// (not necessary if you only have a single task)!
def addXjcTask(taskName, schema, pkg, dest) {
    // If you haven't already, create the generated output dir before running XJC or it will fail
    file(dest).mkdirs()

    // The main XJC task, calls XJCFacade which is the entry point of the XJC JAR
    tasks.create(name: taskName, type: JavaExec) {

        classpath configurations.jaxb
        mainClass 'com.sun.tool.xjc.XJCFacade'

        // To explore available args, download the XJC JAR manually and run java -jar jaxb-xjc.jar --help
        args schema, "-p", pkg, "-d", dest "-no-header"
    }

    // Add a dependency on the new task so it gets invoked
    // >> Comment out to see if this works // compileGeneratedJava.dependsOn tasks.getByName(taskName)
}

// Add all the XJC tasks you need
addXjcTask("UniversalObjectLCC202210v05.02.00.xsd",
    "${projectDir}/src/main/resources/xsd/iso/SomeXSDFile.xsd",
    "com.company.iso.v05200",
    "$generated_dir")

tasks.register("generateSources"){
    description "Generate Java sources from XSD files"
    dependsOn tasks.getByName("UniversalObjectLCC202210v05.02.00.xsd")
}

// END jaxb convert XSD to Java Classes

@Vampire are you out there??? :slight_smile:

Always, but that’s no reason to be rude.
Pinging or DMing a person just because you think they could have an answer is rude in practically any open community out there, unless some written rules allow it, or you really want something personal from that person.

I get the following:

That’s the problem if you follow outdated information.
Many information about Gradle you find out there is stale, or was following back then state-of-the art best practices but might be discouraged bad practice now.
So if you take advice from the internet about Gradle, always keep an eye on when / for what version this advice was given.
Gradle is still evolving pretty fast, so what was fine yesterday, might be really bad tomorrow.

The error you get is, because you try to add a dependency to the compile configuration. This configuration was deprecated many many many years and finally was removed in Gradle 7.0. So the things you look at were written for Gradle <7 but you use Gradle 7 or newer where this configuration does not exist anymore which is why it is failing.

Besides that,

  • adding src/main/java to the generated source set is a pretty bad idea, that will mean you compile those sources twice and later have problems with duplicated class files when assembling things together
  • having a generated source set is not really a good idea at all anyway, it is totally unnecessary, just make sure the task that generates the files has properly configured its inputs and outputs, then define the task itself as srcDir of the main source set. This way the output of the task will be considered sources of the main source set and you also get the necessary task dependencies for all tasks that need to consume sources like compileJava, javadoc, sourceJar, and so on automatically where needed
  • you should probably neither add the jaxb-runtime runtime to the jaxb configuration, nor the xjc dependency to the implementation configuration
  • after following the previous points, your second dependencies { ... } block should be obsolete and thus your error also gone
  • don’t create the destination directory at configuration phase. If you properly register it as output direcotry for the task, Gradle should automatically create it for you. If not, then at least to it in a doFirst { ... } action instead, so that it is done when the task executes, not at configuration time which can have several issues
  • do not use tasks.create but tasks.register to leverage task-configuration avoidance
  • never add an explicit dependsOn except with a lifecycle task on the left-hand side. Your generateSources task might be considered a lifecycle task if it is meant for you to trigger various code generation tasks by invoking just that task. But the compileGeneratedJava task would not qualify. Any explicit dependsOn where the left-hand side is not a lifecycle task is a code smell and usually a sign that you somewhere not properly wired task outputs to task inputs but manually configured paths which you should avoid
  • as you did not share what generated_dir is just to make sure, each task should have outputs only for itself and not have overlapping outputs with any other task or really bad things will happen silently or loudly, so make sure this is a directory exclusively for the outputs of that single task
  • more a personal but strong advice in the end, switch to Kotlin DSL. By now it is the default DSL, you immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio
1 Like

Vampire, I did not mean to be rude at all. I hold you in HIGH REGARD with knowledgeable and although virtual, I consider you my Gradle Mentor. I took 3 hours yesterday and followed the Gradle Docs Core Concepts - Gradle Basics and Tutorial - Part 1: Initializing the Project. I cannot begin to say the very bad practice I am looking at for the Gradle configurations in projects I am working on at work. The non-use of

libs.version.toml
settings.gradle

for plugins

and build.gradle using libs.version.toml and settings.gradle

Maybe it has just been from older versions, but Gradle has come a long way. Great documentation and great 3 hours to my day were used.

Keep up your great help to the Gradle community. I greatly appreciate it.

I will take you suggestions from the previous post and work on hopefully getting all the XSD’s to to JAVA because I am becoming a impediment. My task has been to convert from MAVEN to Gradle.

1 Like

Well, maybe some of the existing plugins is fine and you can just use that?
There are quiet some and some also had recent releases, so maybe are good for recent Gradle versions.

I think it will be most helpful to provide what I am trying to accomplish here >>>

File Structure

  • Root
    |
    • src
    • build
      |
    • main
      |
      • java
      • resources
        |
        • xsd
          |
          • cyber
            - Schema.CYBER..xsd
            - Scehma.CYBER.
            .xsd
          • And other and other xsd files
      • xjb (Just one file SCHEMA_README.txt

[quote=“Vampire, post:2, topic:49338”]

  • adding src/main/java to the generated source set is a pretty bad idea, that will mean you compile those sources twice and later have problems with duplicated class files when assembling things together [moojjoo - So is it better for the generated_dir to be build >>> destination vs src > main > java (Is this causing a double compile?]
  • having a generated source set is not really a good idea at all anyway, it is totally unnecessary, just make sure the task that generates the files has properly configured its inputs and outputs [moojjoo to research], then define the task itself as srcDir of the main source set. This way the output of the task will be considered sources of the main source set and you also get the necessary task dependencies for all tasks that need to consume sources like compileJava, javadoc, sourceJar, and so on automatically where needed
  • you should probably neither add the jaxb-runtime runtime to the jaxb configuration, nor the xjc dependency to the implementation configuration
  • after following the previous points, your second dependencies { ... } block should be obsolete and thus your error also gone [moojjoo will remove]
  • don’t create the destination directory at configuration phase. If you properly register it as output direcotry for the task, Gradle should automatically create it for you. If not, then at least to it in a doFirst { ... } action instead, so that it is done when the task executes, not at configuration time which can have several issues [moojjoo good to know]
  • do not use tasks.create but tasks.register [moojjoo will follow this practice] to leverage task-configuration avoidance
  • never add an explicit dependsOn except with a lifecycle task on the left-hand side. Your generateSources task might be considered a lifecycle task if it is meant for you to trigger various code generation tasks by invoking just that task. But the compileGeneratedJava task would not qualify. Any explicit dependsOn where the left-hand side is not a lifecycle task is a code smell and usually a sign that you somewhere not properly wired task outputs to task inputs but manually configured paths which you should avoid
  • as you did not share what generated_dir is just to make sure, each task should have outputs only for itself and not have overlapping outputs with any other task or really bad things will happen silently or loudly, so make sure this is a directory exclusively for the outputs of that single task
  • more a personal but strong advice in the end, switch to Kotlin DSL. By now it is the default DSL, you immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio
    [/quote] [moojjoo if time permits, I will convert, but again I am being an impediment and have to get this working]

You should look at the formatting help of the forum
I initially thought you just copied my full comment and did not add anything, until I’ve seen the inline comments.
Better use quote, then answer style like:

So is it better for the generated_dir to be build >>> destination vs src > main > java

I would never generate into existing checked in sources, that pollutes the checked in sources.
Always generate into layout.buildDirectory where also any other build artifact should go to.
And as I said, make sure that each task has its own dedicated outputs without any overlap.

Is this causing a double compile?

I don’t know what you mean.
If you think you configured with that part where to generate to, you didn’t.
You added the src/main/java as source directory to the generated source set you added and thus did compile all files in src/main/java twice.


If you hid any other comments in this big quote, I didn’t see them.
And I also have no idea what you tried to say with your sketched directory tree.

1 Like

Thank you so much as always for the reply.

Ready for any critiquing.

// Tring my best to convert XSD to JAVA using jaxb

// Above in the build.gradle I have the following implementations, that are more, but did not include


def generated_dir = "src/main/java"
sourceSets {
    generated {
           java.srcDir "$gerated_dir"
}

// JAB2 configuration holds classpath for running the JAXB XJC compiler
configurations {
        jaxb
}

dependencies {
    jaxb "org.glassfish.jaxb:jaxb-xjc:4.0.1"
    jaxb "org.glassfish.jaxb:jaxb-runtime:4.0.1"

   implementation "org.glassfish.jaxb:jaxb-runtime:4.0.1"
   implementation 'jakartaxml.bind:jakarta.xml.bind-api:4.0.0'
}

def addXjcTask(taskName, schema pkg, dest) {
     file(dest).mkdirs()

tasks.create(name: taskName, type: JavaExec){
        classpath configurations.jaxb
        mainClass = 'com.sun.tools.xjc.XJCFacade'

        // To explore available args download the xjc JAR manually and run java -jar jaxb-xjc.jar --help
        agrs schema, "-p", pkg, "-d", dest
}

// add a dependency on the new task so it get invoked
tasks.register("convert" {
    dependsOn tasks getByName(taskName)
  }
}

addXjcTask("xjc-upo",
                    "${project.rootDir}/src/main/resources/xsd/upo/file.xsd",
                    "com.company.xsd.upo.12345",
                     "${generated_dir"
tasks.register("generateSource01, Task) {
   description "Generate Java sources from XSD files"
   dependsOn tasks.getByName("xjc-upo")
)

addXjcTask("xjc-upo2",
                    "${project.rootDir}/src/main/resources/xsd/upo/file2.xsd",
                    "com.company.xsd.upo.12345",
                     "${generated_dir"
tasks.register("generateSource02, Task) {
   description "Generate Java sources from XSD files"
   dependsOn tasks.getByName("xjc-upo2")
)

this does work with ./gradlew task gernateSource01

but I have to repeat for each generateSource01, 02, etc. etc.

How can I fire all the gererateSource## on ./gradlew build?

If you ask a question with code, you should really not re-type your code, but copy it.
Because why the shown snippet does not work is simple, it does not even compile due to several reasons.
So it is a bit hard to give advice on code that might be what you actually have.

But besides that, are you aware that you ignored most things I told you?
Let me repeat:

  • adding src/main/java to the generated source set is a pretty bad idea, that will mean you compile those sources twice and later have problems with duplicated class files when assembling things together
  • having a generated source set is not really a good idea at all anyway, it is totally unnecessary, just make sure the task that generates the files has properly configured its inputs and outputs, then define the task itself as srcDir of the main source set. This way the output of the task will be considered sources of the main source set and you also get the necessary task dependencies for all tasks that need to consume sources like compileJava, javadoc, sourceJar, and so on automatically where needed
  • you should probably not add the jaxb-runtime runtime to the jaxb configuration (or does the genration fail then? as far as I remember the runtime is only needed … well … at runtime)
  • I said “after following the previous points, your second dependencies { ... } block should be obsolete and thus your error also gone”, but you deleted it without following the previous points, so is probably part of your problem, hard to say as you said yourself you only provided an excerpt
  • don’t create the destination directory at configuration phase. If you properly register it as output direcotry for the task, Gradle should automatically create it for you. If not, then at least to it in a doFirst { ... } action instead, so that it is done when the task executes, not at configuration time which can have several issues
  • do not use tasks.create but tasks.register to leverage task-configuration avoidance
  • each individual task should have outputs only for itself and not have overlapping outputs with any other task or really bad things will happen silently or loudly, so make sure this is a directory exclusively for the outputs of that single task (especially after you followed the second point and properly declared the output of the task as you should

And additionally new:

  • Why do you now create two tasks for each schema? The xjc-upoX task and the generateSourceX task, that does not make much sense to me.
  • declare the inputs and outputs of your task, or it can never be up-to-date and always has to run, besides that you anyway need it for point two above

but I have to repeat for each generateSource01, 02, etc. etc.

If you want to have one lifecycle task that you can call manually, just have one lifecycle task, that depends on all the generation tasks. As I said above, it anyway does not make much sense to me why you create two tasks per schema.

How can I fire all the gererateSource## on ./gradlew build ?

As I already explained, follow my points above and it just works, especially the first two bullet points.

I am re-typing because my work system is off limits and I have to scrub everything.

I am going to fix this, it is currently 5:40 AM ET and I have not slept all night getting this to work. :sleepy:

The jaxb-runtime is working

Can you provide more insight on this are you saying I should not

def generated_dir = "src/main/java"
sourceSets {
    generated {
           java.srcDir "$gerated_dir"
}

I have 32 XSD files I am converting in different directory structure /xsd/ABC, /xsd/DEF, etc.

I have created 32

The following was changed to include encoding

def addXjcTask(taskName, schema pkg, dest, **encoding**) {
     file(dest).mkdirs()

tasks.create(name: taskName, type: JavaExec){
        classpath configurations.jaxb
        mainClass = 'com.sun.tools.xjc.XJCFacade'

        // To explore available args download the xjc JAR manually and run java -jar jaxb-xjc.jar --help
        agrs schema, "-p", pkg, "-d", dest, **"-encoding", encoding**
}
addXjcTask("xjc-upo2",
                    "${project.rootDir}/src/main/resources/xsd/upo/file2.xsd",
                    "com.company.xsd.upo.12345",
                     "${generated_dir",
                     "UTF-8" /**/ Yes this was fun dealing with WINDOWS**
tasks.register("generateSource02, Task) {
   description "Generate Java sources from XSD files"
   dependsOn tasks.getByName("xjc-upo2")
)

My BIG BIG question is there a way to loop the tasks to all run with ./gradlew build

Now I am running
./gradlew task generateSource01 repeating 32 times.

I am re-typing because my work system is off limits and I have to scrub everything.

If you can, use a build --scan, but I guess that also does not work out.
If you have an Android phone, I can recommend Google Lens.
With that you can just point your camera at the text and then are able to copy it as plain text.

The jaxb-runtime is working

I didn’t say it is not working, I just said it should be unnecessary and thus just make things slower and less clean.

Can you provide more insight on this are you saying I should not

def generated_dir = "src/main/java"
sourceSets {
    generated {
           java.srcDir "$gerated_dir"
}

Your reaction does not at all match to the quote you made.
The quote says “do not call mkdirs() at configuration time, but at most at execution time in a doFirst { ... } block”, beside that it also says that you do not need to create it at all if you properly register the directory as output directory of the task as then Gradle will create the directory for you automatically.

Your response talks about a completely different part of your code that is not creating any directory.
But yes, I also said you should not do that in the other bullet poitns. Neither generating into src/main/java, nor adding a source set called generated, and definitely not adding src/main/java as source directory to the generated source set.

My BIG BIG question is there a way to loop the tasks to all run with ./gradlew build

My even BBIIGGEERR answer is, that I already told you multiple times what to do, but you ignore the points I give you. If you would follow them, you would not need to specify a single task, but it would simply work. I account your ignorance to your lack of sleep and hope for you that a good nap will make you more able to read and follow instructions. :wink:

Just in case it helps, I converted a legacy project using XSD (and WSDL) code generation from Ant to Gradle some years ago, and found bjornvester’s plugins (GitHub - bjornvester/xjc-gradle-plugin: A Gradle plugin for running the XJC binding compiler to generate JAXB Java source code from XSD schemas and GitHub - bjornvester/wsdl2java-gradle-plugin: A Gradle plugin for generating Java classes from WSDL files) to be super useful in that regard.