Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generating json schema from generated classes out of protobuf request #369

Open
Rustem opened this issue Jul 25, 2023 · 2 comments
Open
Labels
wontfix This will not be worked on

Comments

@Rustem
Copy link

Rustem commented Jul 25, 2023

Hi team, could you please help me with few issues related to protobuf->java object -> json schema using your tool. I tried to generate json schema out of protobuf generated java class.
It comes with you extra fields and some unnecessary fields that i d like to skip.

it generates fields ending with _ because the class implements them this way. For example generated class for field drid has private field drid_ and has public function getDrid(). I tried different presets, it does not work.
Another issue is additional fields like memoizedIsInitialized and memoizedHashCode that i d like to exclude from generated schema.

Example proto:

message CancelRequest {

  google.protobuf.Int32Value podNumber = 1;

  google.protobuf.Int64Value drid = 2; 

  google.protobuf.Int64Value subscriptionId = 3; 

}

Example code

public class JsonSchemaGeneratorDemo {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(mapper, SchemaVersion.DRAFT_7, OptionPreset.PLAIN_JSON);
        SchemaGenerator generator = new SchemaGenerator(configBuilder.build());

        JsonNode jsonSchema = generator.generateSchema(CancelRequest.class);
        System.out.println(jsonSchema.toPrettyString());
    }
}

Example output:

{
  "$schema" : "https://json-schema.org/draft/2020-12/schema",
  "$defs" : {
    "Int32Value" : {
      "type" : "object",
      "properties" : {
        "memoizedHashCode" : {
          "type" : "integer"
        },
        "memoizedIsInitialized" : {
          "type" : "string"
        },
        "memoizedSize" : {
          "type" : "integer"
        },
        "unknownFields" : {
          "$ref" : "#/$defs/UnknownFieldSet"
        },
        "value_" : {
          "type" : "integer"
        }
      }
    },
    "Int64Value" : {
      "type" : "object",
      "properties" : {
        "memoizedHashCode" : {
          "type" : "integer"
        },
        "memoizedIsInitialized" : {
          "type" : "string"
        },
        "memoizedSize" : {
          "type" : "integer"
        },
        "unknownFields" : {
          "$ref" : "#/$defs/UnknownFieldSet"
        },
        "value_" : {
          "type" : "integer"
        }
      }
    },
    "UnknownFieldSet" : {
      "type" : "object",
      "properties" : {
        "fields" : {
          "type" : "object",
          "properties" : {
            "comparator" : {
              "type" : "object"
            }
          }
        }
      }
    }
  },
  "type" : "object",
  "properties" : {
    "drid_" : {
      "$ref" : "#/$defs/Int64Value"
    },
    "memoizedHashCode" : {
      "type" : "integer"
    },
    "memoizedIsInitialized" : {
      "type" : "string"
    },
    "memoizedSize" : {
      "type" : "integer"
    },
    "podNumber_" : {
      "$ref" : "#/$defs/Int32Value"
    },
    "subscriptionId_" : {
      "$ref" : "#/$defs/Int64Value"
    },
    "unknownFields" : {
      "$ref" : "#/$defs/UnknownFieldSet"
    }
  }
}
@CarstenWickner
Copy link
Member

Hi @Rustem,

Can you provide an example for the generated Java class? I didn't have this requirement myself yet, to generate a JSON schema based on a generated Java class from a protobuf description.
I'm wondering whether a direct conversion would be a cleaner option?
E.g. using existing libraries, like these:

That being said, there are ways to "clean up" the JSON schema for the generated classes here, e.g.,

  • ignoring certain fields (memoizedHashCode, memoizedIsInitialized, memoizedSize, unknownFields)
  • use a TargetTypeOverride to map the standard protobuf types (Int32Value, Int64Value, etc.) to their simpler Java equivalents (e.g., Integer/Long)
  • apply a PropertyNameOverride to discard the trailing _ or merely include the getter methods and derive the correct fields from there.

To not waste my time, in case you opt for the direct conversion, I'll not go into more detail for now. 😉

Let me know, if you still want better hints for the points above.

@Rustem
Copy link
Author

Rustem commented Jul 27, 2023

Thanks @CarstenWickner , I explored those libraries your referenced. In fact my current implementation is based on protoc-gen-jsonschema, but it works only in compile time. To scale the system i need runtime java protobuf-jsonschema generator and yours is the best I found so far. Would you mind to share how to ignore certain fields? I found in your documentation that i can do something like this?

Btw , I think use-case like mine when we use your lib to transform some schema to a json-schema through generated java classes could be a good use-case.

configBuilder.forFields()
    .withIgnoreCheck(field -> field.getName().startsWith("_"));
configBuilder.forMethods()
    .withIgnoreCheck(method -> !method.isVoid() && method.getType().getErasedType() == Object.class);

Also sharing a java class with all relevant information:

 public static final class CancelRequest extends
      com.google.protobuf.GeneratedMessageV3 implements
      // @@protoc_insertion_point(message_implements:CancelRequest)
      CancelRequestOrBuilder {
  private static final long serialVersionUID = 0L;
    // Use CancelRequest.newBuilder() to construct.
    private CancelRequest(com.google.protobuf.GeneratedMessageV3.Builder<?> builder) {
      super(builder);
    }
    private CancelRequest() {
    }

    @java.lang.Override
    protected java.lang.Object newInstance(
        UnusedPrivateParameter unused) {
      return new CancelRequest();
    }

    @java.lang.Override
    public final com.google.protobuf.UnknownFieldSet
    getUnknownFields() {
      return this.unknownFields;
    }
    
    public static final int PODNUMBER_FIELD_NUMBER = 1;
    private com.google.protobuf.Int32Value podNumber_;
    
    @java.lang.Override
    public boolean hasPodNumber() {
      return podNumber_ != null;
    }
    
    @java.lang.Override
    public com.google.protobuf.Int32Value getPodNumber() {
      return podNumber_ == null ? com.google.protobuf.Int32Value.getDefaultInstance() : podNumber_;
    }
   
    @java.lang.Override
    public com.google.protobuf.Int32ValueOrBuilder getPodNumberOrBuilder() {
      return getPodNumber();
    }

    public static final int drid_FIELD_NUMBER = 2;
    private com.google.protobuf.Int64Value drid_;
    
    @java.lang.Override
    public boolean hasdrid() {
      return drid_ != null;
    }
   
    @java.lang.Override
    public com.google.protobuf.Int64Value getdrid() {
      return drid_ == null ? com.google.protobuf.Int64Value.getDefaultInstance() : drid_;
    }
    
    @java.lang.Override
    public com.google.protobuf.Int64ValueOrBuilder getdridOrBuilder() {
      return getdrid();
    }

    public static final int SUBSCRIPTIONID_FIELD_NUMBER = 3;
    private com.google.protobuf.Int64Value subscriptionId_;
   
    @java.lang.Override
    public boolean hasSubscriptionId() {
      return subscriptionId_ != null;
    }
    
    @java.lang.Override
    public com.google.protobuf.Int64Value getSubscriptionId() {
      return subscriptionId_ == null ? com.google.protobuf.Int64Value.getDefaultInstance() : subscriptionId_;
    }
    
    @java.lang.Override
    public com.google.protobuf.Int64ValueOrBuilder getSubscriptionIdOrBuilder() {
      return getSubscriptionId();
    }

    private byte memoizedIsInitialized = -1;
    @java.lang.Override
    public final boolean isInitialized() {
      byte isInitialized = memoizedIsInitialized;
      if (isInitialized == 1) return true;
      if (isInitialized == 0) return false;

      memoizedIsInitialized = 1;
      return true;
    }

  }

@CarstenWickner CarstenWickner added the wontfix This will not be worked on label Sep 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Development

No branches or pull requests

2 participants