Java 如何从JsonParser(Jackson Json)获取底层字符串
通过查看文档和源代码,我看不到一个明确的方法来做到这一点。我很好奇我是否遗漏了什么 假设我从服务器响应接收到InputStream。我从这个InputStream创建了一个JsonParser。服务器响应应该是包含有效JSON的文本,例如:Java 如何从JsonParser(Jackson Json)获取底层字符串,java,android,json,jackson,inputstream,Java,Android,Json,Jackson,Inputstream,通过查看文档和源代码,我看不到一个明确的方法来做到这一点。我很好奇我是否遗漏了什么 假设我从服务器响应接收到InputStream。我从这个InputStream创建了一个JsonParser。服务器响应应该是包含有效JSON的文本,例如: {“iamValidJson”:“耶”} 但是,如果响应最终是无效的JSON或根本不是JSON,例如: 一些非JSON的文本 JsonParser最终将抛出一个异常。在本例中,我希望能够从JsonParser中提取底层无效文本“一些非JSON的文本”,以便将
{“iamValidJson”:“耶”}
但是,如果响应最终是无效的JSON或根本不是JSON,例如:
一些非JSON的文本
JsonParser最终将抛出一个异常。在本例中,我希望能够从JsonParser中提取底层无效文本“一些非JSON的文本”
,以便将其用于其他目的
我无法将其从InputStream中拉出,因为它不支持重置,而JsonParser的创建会消耗它
有办法做到这一点吗?如果您有
JsonParser
,那么您可以使用JsonParser.readValueAsTree().toString()
但是,这可能需要解析的JSON确实是有效的JSON。您试图做的事情超出了Jackson的范围(如果不是所有其他Java JSON库的话,也是大多数)。您要做的是将输入流完全转换为字符串,然后尝试使用Jackson将该字符串转换为JSON对象。如果转换失败,则对中间字符串执行某些操作,否则将正常进行。下面是一个例子,为了方便起见,它使用了优秀的:
final InputStream stream ; // Your stream here
final String json = IOUtils.toString(stream);
try {
final JsonNode node = new ObjectMapper().readTree(json);
// Do something with JSON object here
} catch(final JsonProcessingException jpe) {
// Do something with intermediate string here
}
我曾经使用过一个自定义反序列化程序,但我希望使用默认的反序列化程序来完成大部分工作,然后使用相同的json来完成一些额外的自定义工作。但是,在默认反序列化程序完成其工作后,JsonParser对象的当前位置超出了我需要的json文本。因此,我遇到了与您相同的问题:如何访问底层json字符串 您可以使用
JsonParser.getCurrentLocation.getSourceRef()
访问底层json源代码。使用JsonParser.getCurrentLocation().getCharOffset()
查找json源中的当前位置
以下是我使用的解决方案:
public class WalkStepDeserializer extends StdDeserializer<WalkStep> implements
ResolvableDeserializer {
// constructor, logger, and ResolvableDeserializer methods not shown
@Override
public MyObj deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException,
JsonProcessingException {
MyObj myObj = null;
JsonLocation startLocation = jp.getCurrentLocation();
long charOffsetStart = startLocation.getCharOffset();
try {
myObj = (MyObj) defaultDeserializer.deserialize(jp, ctxt);
} catch (UnrecognizedPropertyException e) {
logger.info(e.getMessage());
}
JsonLocation endLocation = jp.getCurrentLocation();
long charOffsetEnd = endLocation.getCharOffset();
String jsonSubString = endLocation.getSourceRef().toString().substring((int)charOffsetStart - 1, (int)charOffsetEnd);
logger.info(strWalkStep);
// Special logic - use JsonLocation.getSourceRef() to get and use the entire Json
// string for further processing
return myObj;
}
}
public类WalkStepDeserializer扩展StdDeserializer实现
可解析反序列化器{
//未显示构造函数、记录器和可解析反序列化器方法
@凌驾
公共MyObj反序列化(JsonParser jp,反序列化上下文ctxt)抛出IOException,
JsonProcessingException{
MyObj MyObj=null;
JsonLocation startLocation=jp.getCurrentLocation();
long charOffsetStart=startLocation.getCharOffset();
试一试{
myObj=(myObj)defaultDeserializer.deserialize(jp,ctxt);
}捕获(无法识别的属性异常e){
logger.info(例如getMessage());
}
JsonLocation endLocation=jp.getCurrentLocation();
long charOffsetEnd=endLocation.getCharOffset();
字符串jsonSubString=endLocation.getSourceRef().toString().substring((int)charOffsetStart-1,(int)charOffsetEnd);
logger.info(strWalkStep);
//特殊逻辑-使用JsonLocation.getSourceRef()获取并使用整个Json
//用于进一步处理的字符串
返回myObj;
}
}
关于在自定义反序列化程序中使用默认反序列化程序的信息迟了5年,但这是我的解决方案: 我将jsonParser转换为字符串
String requestString = jsonParser.readValueAsTree().toString();
然后我将该字符串转换为JsonParser
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(requestString);
然后我遍历了我的解析器
ObjectMapper objectMapper = new ObjectMapper();
while(!parser.isClosed()){
JsonToken jsonToken = parser.nextToken();
if(JsonToken.FIELD_NAME.equals(jsonToken)){
String currentName = parser.getCurrentName();
parser.nextToken();
switch (currentName) {
case "someObject":
Object someObject = objectMapper.readValue(parser, Object.class)
//validate someObject
break;
}
}
我需要保存原始json字符串以用于日志记录,这就是为什么我首先这么做的原因。很难找到答案,但最终还是找到了答案,我希望我正在帮助别人:)构建我自己的反序列化程序,其中我希望将特定字段反序列化为文本I.s.o。一个合适的DTO,这就是我提出的解决方案 我编写了自己的JSONTStringDeserializer,如下所示:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringEscapeUtils;
import java.io.IOException;
/**
* Deserialiser to deserialise any Json content to a String.
*/
@NoArgsConstructor
public class JsonToStringDeserializer extends JsonDeserializer<String> {
/**
* Deserialise a Json attribute that is a fully fledged Json object, into a {@link String}.
* @param jsonParser Parsed used for reading JSON content
* @param context Context that can be used to access information about this deserialization activity.
* @return The deserialized value as a {@link String}.
* @throws IOException
*/
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
final TreeNode node = jsonParser.getCodec().readTree(jsonParser);
final String unescapedString = StringEscapeUtils.unescapeJava(node.toString());
return unescapedString.substring(1, unescapedString.length()-1);
}
}
final TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
return treeNode.toString();
我最初遵循的建议是使用如下树节点:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringEscapeUtils;
import java.io.IOException;
/**
* Deserialiser to deserialise any Json content to a String.
*/
@NoArgsConstructor
public class JsonToStringDeserializer extends JsonDeserializer<String> {
/**
* Deserialise a Json attribute that is a fully fledged Json object, into a {@link String}.
* @param jsonParser Parsed used for reading JSON content
* @param context Context that can be used to access information about this deserialization activity.
* @return The deserialized value as a {@link String}.
* @throws IOException
*/
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
final TreeNode node = jsonParser.getCodec().readTree(jsonParser);
final String unescapedString = StringEscapeUtils.unescapeJava(node.toString());
return unescapedString.substring(1, unescapedString.length()-1);
}
}
final TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
return treeNode.toString();
但是您会得到一个包含转义字符的Json字符串。我们在
JsonDeserializer
中使用此方法,使用org.geotools.geojson.geom.GeometryJSON
解析器解析geojson
对象,因为它只是直接解析Json字符串。“然而,这可能需要解析的JSON确实是有效的JSON”如果您将内容类型设置为application/JSON
,那么您确实应该发送JSON格式的内容。也许您可以将其更改为{“textDescription”:“text”}
替换为“textDescription”“无论在您的特定情况下有什么意义。但是请注意,Jackson可以解析任何JSON值,它不限于JSON文本(即,仅数组或对象)。事实上,大多数解析器都可以,即使RFC理论上禁止这样做。仅供参考,带引号的字符串或普通数字,甚至常量值true
,false
和null
都是有效的JSON。请注意,您可以要求JsonParser
不要关闭底层输入流。我相信您关于它超出范围的说法是正确的。我只是希望有其他的解决方案,因为这样做会破坏使用流式解析器的性能优势。但是,您的解决方案确实有效。流解析程序假定传入流是有效的(它必须这样做,因为它在令牌推送模式下运行)。出于与您提到的相同的性能原因,它不会“缓存”已处理的内容,它只是检查是否有有效的令牌,如果有,则将其发送到链中的下一个处理程序。如果没有,它就会发出嘎嘎声。另一个解决方案是实现一个累积流,并将其链接到您使用的任何输入流上。不一定是干净的,但它可以减轻您对流媒体性能的担忧