I'm currently trying to use jackson-dataformat-cbor:2.19.0
and want to parse custom tags. Since I couldn't find specific examples, I attempted to implement it with the following code.
public class CborParserDelegate extends JsonParserDelegate {
protected final CBORParser delegate;
public CborParserDelegate(CBORParser d) {
super(d);
this.delegate = d;
}
@Override
public CBORParser delegate() {
return (CBORParser) super.delegate();
}
public int getCurrentTag() {
return delegate().getCurrentTag();
}
public CBORParser.TagList getCurrentTags() {
return delegate().getCurrentTags();
}
}
public class CustomTagsCborParser extends CborParserDelegate {
public CustomTagsCborParser(CBORParser d) {
super(d);
try {
this.method = ParserMinimalBase.class.getDeclaredMethod("_updateToken", JsonToken.class);
this.method.setAccessible(true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final Method method;
private Object customValue;
@Override
public JsonToken nextToken() throws IOException {
JsonToken jsonToken = super.nextToken();
int currentTag = delegate.getCurrentTag();
if (currentTag != -1) {
ObjectCodec codec = getCodec();
this.customValue = mapValue(currentTag, codec.readTree(delegate));
return Err.call(() -> (JsonToken) method.invoke(delegate, JsonToken.VALUE_EMBEDDED_OBJECT));
}
return jsonToken;
}
@Override
public Object getEmbeddedObject() throws IOException {
if (this.customValue != null) {
Object v = this.customValue;
this.customValue = null;
return v;
}
return super.getEmbeddedObject();
}
private Object mapValue(int tag, TreeNode treeNode) {
CustomTag customTag = CustomTag.of(tag);
switch (customTag) {
case TAG_12: {
_assert(treeNode.isArray(), "TAG_12 should be an array with two items.");
ArrayNode arrayNode = (ArrayNode) treeNode;
long seconds = arrayNode.get(0).asLong(0);
long nanoSeconds = arrayNode.get(1).asLong(0);
return Instant.ofEpochSecond(seconds, nanoSeconds);
}
case TAG_14: {
_assert(treeNode.isArray(), "TAG_14 should be an array with two items.");
ArrayNode arrayNode = (ArrayNode) treeNode;
long seconds = arrayNode.at("/0").asLong(0);
long nanoSeconds = arrayNode.at("/1").asLong(0);
return Duration.ofSeconds(seconds, nanoSeconds);
}
default:
return treeNode;
}
}
private void _assert(boolean state, String message) {
if (!state) {
throw new IllegalStateException(message);
}
}
}
However, since CborParser
inherits from ParserMinimalBase
, calling its _updateToken(JsonToken)
method requires reflection, which I'd like to avoid.
So my questions are:
- Is my approach for custom tag parsing with dataformat-cbor correct (or aligned with your design)? If not, what should I do instead?
- Is there a way to avoid using reflection when updating tokens?
Comment From: cowtowncoder
I think you have basically hit an unsupported use case, and I suspect your work-arounds are unfortunately required. _updateToken()
is not designed to be accessible from outside class (or super-class.
Ideally I think there would be a pluggable extension point/handler of some kind to help with custom tags, but there isn't anything right now.
Comment From: honhimW
@cowtowncoder Well, it seems that for now, reflection is required.
BTW, although this question should probably be posted in the jackson-dataformats-binary. If it's convenient for you, could you let me know if there are any code repositories using jackson-cbor to handle custom tags that I could refer to? I've tried many searches but couldn't find any concrete examples, even though this should be a relatively common feature in CBOR.
Comment From: cowtowncoder
@honhimW I am not aware of other repositories doing that, you are the first one. You are probably right ones exists but I haven't been contacted by anyone else.
I will file a general issue for adding such extension point, as a discussion starter.
EDIT: https://github.com/FasterXML/jackson-dataformats-binary/issues/581