docs(readme): document SDK JSON mapper - #109
Conversation
The JSON serialization snippet referenced an undefined `request` and did not show the read direction, though the section text promises "request or response objects". Add the builder call that defines `request`, add a `readValue` example for parsing a response payload, and note that both mapper calls throw `JsonProcessingException`. Unwrap the paragraph to one line to match the rest of the README. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Approving. The claim checks out, and I verified it by running it rather than reading it.
What I checked
- Does a plain mapper really fail? Yes. Serializing a
V1ConnectOptionswithnew ObjectMapper()throws exactly the exception this section names:InvalidDefinitionException: Java 8 optional type java.util.Optional<com.deepgram.types.ListenV1Callback> not supported by default.JSON_MAPPERhandles the same object fine. - Is "can throw" the right hedge, rather than "will throw"? Yes, and it matters. A
ListenV1RequestUrlwith onlyurlset serializes cleanly on a plain mapper, because no optional field is populated. It fails for some objects and not others, so "can" is accurate as written. - Does the mapper register what the text says it registers? Yes —
ObjectMappers.java:18-19addsJdk8ModuleandJavaTimeModule, precisely the two things the sentence names. - Are the date/time fields real, or is that half hypothetical? Real. 16 files under
src/main/javausejava.time; e.g.CreateKeyV1Response.java:35holds anOptional<OffsetDateTime> expirationDate.
Two fixes I pushed (3c4471b)
- The snippet couldn't be pasted.
requestwas never defined in the section, andwriteValueAsStringthrows a checkedJsonProcessingException. Worth knowing:ReadmeSnippetsCompileTestcannot catch either problem — it injectsrequestas a static field (ReadmeSnippetsCompileTest.java:171) and wraps every snippet incompileOnly() throws Exception(:213), so a snippet can pass the test while failing a literal copy-paste. The snippet now definesrequestvia a builder call, and a comment flags the checked exception (matching how the README already handlesFiles.readAllBytesandThread.sleep). - The first sentence promises "request or response objects" but only the write direction was shown, and parsing is the more common need. Reading fails identically — a plain
readValueon aCreateKeyV1Responsepayload threw the same exception, whileJSON_MAPPERparsed it including an unrecognized field. Added areadValueline.
All 8 checks pass on the pushed commit.
One question, not blocking
ObjectMappers.java is Fern-generated and is not in .fernignore, so its shape is whatever the generator emits next cycle — we're now telling developers to depend on a class we don't pin. The README already points at four other com.deepgram.core classes (DeepgramTransport, DeepgramTransportFactory, Environment, DeepgramApiHttpResponse), so this is consistent with existing practice rather than something new, and nothing in AGENTS.md or CONTRIBUTING.md declares core internal. Not holding the PR on it — just want your yes that JSON_MAPPER keeps its name and location across regens.
Optional, take or leave
- Every generated type's
toString()already returns pretty-printed JSON viaObjectMappers.stringify(ObjectMappers.java:29), which is the shorter answer for logging and debugging. One sentence would save a reader the mapper call. - The section is reference material sitting inside Quickstart, between auth setup and the feature walkthroughs. It may belong next to "Raw Response Access" where the other plumbing topics live.
|
@dg-coreylweathers Final review items are resolved in 1d2e7e1: |
|
@dg-coreylweathers Correction to my earlier note: the |
dg-coreylweathers
left a comment
There was a problem hiding this comment.
Approving with one small fix and three optional nits. Re-verified by running the snippet, not reading it.
What I checked
- Is the failure claim true? Yes. A plain
new ObjectMapper()parsing the snippet's own payload throwsInvalidDefinitionException: Java 8 optional type java.util.Optional<java.lang.String> not supported by default. The snippet'swriteValueAsStringline actually succeeds on a plain mapper — onlyurlis set, so noOptionalfield is populated — which makes "can throw" the accurate hedge and thereadValueline the one that demonstrates it. - Does the snippet survive a literal copy-paste? Yes. Pasted verbatim into a method declaring no
throwsclause, it compiled and ran. The explicittry/catch (JsonProcessingException)fixes this properly rather than relying onReadmeSnippetsCompileTest'sthrows Exceptionwrapper. - Is the new
toString()claim true? Yes.CreateKeyV1Response.toString()delegates toObjectMappers.stringify, which useswriterWithDefaultPrettyPrinter(); the output came back multiline and re-parsed as valid JSON. (Worth knowing: the .NET SDK's equivalent runsRegex.Unescapeover the output and produces invalid JSON. Java does not.) - Is recommending
JSON_MAPPERandtoString()together safe? Yes, and this needed checking:stringifycallssetSerializationInclusion(ALWAYS)on the shared static mapper, mutating global state. I serialized a request, calledtoString()on a model, and re-serialized — byte-identical, because the class-level@JsonInclude(NON_ABSENT)on each generated model overrides the mapper-level setting. - Is the reference safe against a Fern regen? Yes.
ObjectMappersis generator-owned and not in.fernignore, butReadmeSnippetsCompileTestcompiles this snippet, andRegenTypesTestandAgentSettingsProviderDefaultTestalso referenceObjectMappers.JSON_MAPPER— a rename or relocation fails CI at compile time. That closes my round-1 question more durably than the withdrawn facade would have. - Also verified: the payload's
api_key_id/key/expiration_datematch the model's@JsonPropertynames;CreateKeyV1Responseis genuinely the return type ofmanage().v1().projects().keys().create(...); a repo-wide grep finds no conflicting mapper guidance and no leftoverDeepgramJsonreferences after the revert;mainhas not touchedREADME.mdsince the merge base../gradlew spotlessCheck test compileExamplespassed ineclipse-temurin:17-jdk.
One fix before merge
The section is filed as a subsection of ## Error Handling. A reader scanning the table of contents for serialization guidance finds it only by reading about errors first. Change ### to ## on README.md:661 — it is already the last block before ## Complete SDK Reference, so no text moves, and ReadmeSnippetsCompileTest still collects the snippet (it takes the H2 name when no H3 is open). This is fallout from my own earlier suggestion being followed literally: "next to Raw Response Access" was the right neighborhood, that section just happens to live under Error Handling.
Three nits, take or leave
- README.md:663 — "when serializing SDK request or response objects" covers only the write direction, but the half that fails on a plain mapper is the parse. "when reading or writing SDK request and response objects" is accurate to what the snippet shows.
- README.md:663 — the mapper also disables
FAIL_ON_UNKNOWN_PROPERTIES, which is why it tolerates response fields an older SDK build does not know about. That is the practical reason to use it when parsing an evolving API. - README.md:676 and :680 —
jsonandparsedare assigned and never used; a literal paste emits unused-variable warnings in strict builds.
One note for the record
Your second comment describes DeepgramJson.MAPPER as a permanently frozen facade and your third removes it as unnecessary. The final state is the better one and the compile-time guard above is why. A line saying so would keep the thread from reading as an unexplained reversal.
Keeping the // Never log the returned key value. comment as-is: it sits directly above a payload carrying a key field, which is the one value in a key-creation response that is a live credential returned exactly once.
|
Record clarification: 1d2e7e1 added a facade in response to the generator-stability concern. After confirming that existing compile-time tests already guard |
Fixes three accuracy problems and four copy nits in the JSON section. Accuracy: - Unknown-field tolerance was credited to the mapper. A mapper carrying only Jdk8Module and JavaTimeModule, with FAIL_ON_UNKNOWN_PROPERTIES left at Jackson's default of enabled, parses a payload with an unmodeled field because all 276 generated models declare @JsonIgnoreProperties(ignoreUnknown = true) on their deserialization builder. Restated as a property of the response, and noted that the unrecognized fields survive on additionalProperties. - The toString() sentence offered two outcomes. 270 of 414 classes defining toString() delegate to ObjectMappers.stringify; the other 144 are union and alias wrappers that return the wrapped value instead (SpeakSettingsV1Provider yields "SpeakSettingsV1Provider{value: {...}}", ErrorResponse.of("boom") yields "boom"). Claim scoped accordingly. - The InvalidDefinitionException claim now names a case that reproduces (parsing a CreateKeyV1Response) rather than asserting it for any SDK type; writing a ListenV1RequestUrl with a populated Optional does not throw. Copy: - Heading renamed to "Working with JSON"; the section covers parsing too. - Snippet binds and uses both results, and demonstrates toString(). - Prose split into two shorter paragraphs ending in a colon, matching the surrounding sections. - Redaction warning promoted to the file's > **Note:** callout pattern. ./gradlew spotlessCheck test compileExamples passes in eclipse-temurin:17-jdk; ReadmeSnippetsCompileTest still collects the snippet under the renamed H2, and the snippet compiles and runs pasted verbatim into a method with no throws clause. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Summary
ObjectMappers.JSON_MAPPERas the SDK-configured mapper for request and response objectsOptionaland date/time fieldstoString()output is suitable for pretty-printed debuggingA focused probe confirmed that serializing a generated
V1ConnectOptionsobject with plainnew ObjectMapper()throwsInvalidDefinitionException.Validation
./gradlew spotlessCheck test compileExamplesmvn test