我正在寻找一种方法来解析特定节点的 JSON 文件并获取该节点在文件中的行号。我想使用 Jayway JSONPath 库来支持扩展 JSONPath 查询。
例如(来自 jsonpath.com),这里有一些 JSON:
{
"firstName": "John",
"lastName" : "doe",
"age" : 26,
"address" : {
"streetAddress": "naist street",
"city" : "Nara",
"postalCode" : "630-0192"
},
"phoneNumbers": [
{
"type" : "iPhone",
"number": "0123-4567-8888"
},
{
"type" : "home",
"number": "0123-4567-8910"
}
]
}
这是一个 jsonPath:
$.phoneNumbers.[?(@.type=='iPhone')]
我希望有一种方法可以表明该节点位于 json 文件中的第 11 行。我事先不知道 json 内容或 jsonPath 是什么。两者都是动态的。
到目前为止,我已经尝试将 json 解析为一棵树,并将其遍历到节点以获取解析器的当前位置,但解析器必须始终在 jsonPath 执行之前运行到文件末尾。还有其他想法吗?
最佳答案
我最终找到了一个涉及使用 Jackson 的 JsonFactory 和 JsonParser 的解决方案。至少可以说,这是一种拼凑,但它使用 JsonParser 对其解析器行号的了解来获取 JsonNode 的位置,并且工作得很好。
我将在此处粘贴代码,但代码也可以在 watchtower 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;
}
}
关于java - 在 Java 中给定 JSON 路径/JSON 指针获取 JSON 文件的行号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63585750/