我最终找到了一个涉及使用 Jackson 的 JsonFactory 和 JsonParser 的解决方案。至少可以说是杂乱无章,但它使用 JsonParser 对其解析器行号的了解来获取 JsonNode 的位置并且工作得很好。
我将代码粘贴在这里,但代码也可以在瞭望塔 github 上找到
调用类:
void findLineNumber() throws Exception{
CustomParserFactory customParserFactory = new CustomParserFactory();
ObjectMapper om = new ObjectMapper(customParserFactory);
factory = new CustomJsonNodeFactory(om.getDeserializationConfig().getNodeFactory(),
customParserFactory);
om.setConfig(om.getDeserializationConfig().with(factory));
config = Configuration.builder()
.mappingProvider(new JacksonMappingProvider(om))
.jsonProvider(new JacksonJsonNodeJsonProvider(om))
.options(Option.ALWAYS_RETURN_LIST)
.build();
File filePath = ...;
JsonPath jsonPath = ...;
DocumentContext parsedDocument = JsonPath.parse(filePath, config);
ArrayNode findings = parsedDocument.read(jsonPath);
for (JsonNode finding : findings) {
JsonLocation location = factory.getLocationForNode(finding);
int lineNum = location.getLineNr();
//Do something with lineNum
}
}
CustomJsonNodeFactory.java
public class CustomJsonNodeFactory extends JsonNodeFactory {
private static final long serialVersionUID = 8807395553661461181L;
private final JsonNodeFactory delegate;
private final CustomParserFactory parserFactory;
/*
* "Why isn't this a map?" you might be wondering. Well, when the nodes are created, they're all
* empty and a node's hashCode is based on its children. So if you use a map and put the node
* in, then the node's hashCode is based on no children, then when you lookup your node, it is
* *with* children, so the hashcodes are different. Instead of all of this, you have to iterate
* through a listing and find their matches once the objects have been populated, which is only
* after the document has been completely parsed
*/
private List<Entry<JsonNode, JsonLocation>> locationMapping;
public CustomJsonNodeFactory(JsonNodeFactory nodeFactory,
CustomParserFactory parserFactory) {
delegate = nodeFactory;
this.parserFactory = parserFactory;
locationMapping = new ArrayList<>();
}
/**
* Given a node, find its location, or null if it wasn't found
*
* @param jsonNode the node to search for
* @return the location of the node or null if not found
*/
public JsonLocation getLocationForNode(JsonNode jsonNode) {
return this.locationMapping.stream().filter(e -> e.getKey().equals(jsonNode))
.map(e -> e.getValue()).findAny().orElse(null);
}
/**
* Simple interceptor to mark the node in the lookup list and return it back
*
* @param <T> the type of the JsonNode
* @param node the node itself
* @return the node itself, having marked its location
*/
private <T extends JsonNode> T markNode(T node) {
JsonLocation loc = parserFactory.getParser().getCurrentLocation();
locationMapping.add(new SimpleEntry<>(node, loc));
return node;
}
@Override
public BooleanNode booleanNode(boolean v) {
return markNode(delegate.booleanNode(v));
}
@Override
public NullNode nullNode() {
return markNode(delegate.nullNode());
}
@Override
public NumericNode numberNode(byte v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Byte value) {
return markNode(delegate.numberNode(value));
}
@Override
public NumericNode numberNode(short v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Short value) {
return markNode(delegate.numberNode(value));
}
@Override
public NumericNode numberNode(int v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Integer value) {
return markNode(delegate.numberNode(value));
}
@Override
public NumericNode numberNode(long v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Long value) {
return markNode(delegate.numberNode(value));
}
@Override
public ValueNode numberNode(BigInteger v) {
return markNode(delegate.numberNode(v));
}
@Override
public NumericNode numberNode(float v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Float value) {
return markNode(delegate.numberNode(value));
}
@Override
public NumericNode numberNode(double v) {
return markNode(delegate.numberNode(v));
}
@Override
public ValueNode numberNode(Double value) {
return markNode(delegate.numberNode(value));
}
@Override
public ValueNode numberNode(BigDecimal v) {
return markNode(delegate.numberNode(v));
}
@Override
public TextNode textNode(String text) {
return markNode(delegate.textNode(text));
}
@Override
public BinaryNode binaryNode(byte[] data) {
return markNode(delegate.binaryNode(data));
}
@Override
public BinaryNode binaryNode(byte[] data, int offset, int length) {
return markNode(delegate.binaryNode(data, offset, length));
}
@Override
public ValueNode pojoNode(Object pojo) {
return markNode(delegate.pojoNode(pojo));
}
@Override
public ValueNode rawValueNode(RawValue value) {
return markNode(delegate.rawValueNode(value));
}
@Override
public ArrayNode arrayNode() {
return markNode(delegate.arrayNode());
}
@Override
public ArrayNode arrayNode(int capacity) {
return markNode(delegate.arrayNode(capacity));
}
@Override
public ObjectNode objectNode() {
return markNode(delegate.objectNode());
}
}
CustomParserFactory.java(请注意,这会删除线程安全,这可能很重要):
public class CustomParserFactory extends JsonFactory {
private static final long serialVersionUID = -7523974986510864179L;
private JsonParser parser;
public JsonParser getParser() {
return this.parser;
}
@Override
public JsonParser createParser(Reader r) throws IOException, JsonParseException {
parser = super.createParser(r);
return parser;
}
@Override
public JsonParser createParser(String content) throws IOException, JsonParseException {
parser = super.createParser(content);
return parser;
}
}