Sub document API Mutuate In Json Object Conversion Error

Hi Team,

I am working with Couchbase SDK and I am trying to do the below steps.

  1. I have a sample document.
  2. Get the document Info using sub document API - lookupin Result
  3. Cast the result to Json Object.

Sample Document:

C:ode
public static void main(String args) {
Cluster cluster = Cluster.connect(host, userName, password);
Bucket bucket = cluster.bucket(bucketName);
Collection empCollection = bucket.defaultCollection();
String documentId = “sample123”;
List specs = new ArrayList<>();
specs.add(LookupInSpec.get(“name”));
LookupInResult lookupResult = empCollection.lookupIn(documentId, specs);
JsonObject jsonObject = lookupResult.contentAsObject(0);
System.out.println("jsonObject : " + jsonObject);
}

Exception :

Exception in thread "main" com.couchbase.client.core.error.DecodingFailureException: Deserialization of content into target class com.couchbase.client.java.json.JsonObject failed; encoded = "Employee1"
	at com.couchbase.client.java.codec.DefaultJsonSerializer.deserialize(DefaultJsonSerializer.java:90)
	at com.couchbase.client.java.kv.LookupInResult.contentAs(LookupInResult.java:105)
	at com.couchbase.client.java.kv.LookupInResult.contentAsObject(LookupInResult.java:146)
	at couchbaseRelatedpoc.sample.CouchbaseConnectivityTester.main(CouchbaseConnectivityTester.java:115)
Caused by: java.io.IOException: Expected START_OBJECT but got VALUE_STRING
	at com.couchbase.client.java.json.RepackagedJsonValueModule.expectCurrentToken(RepackagedJsonValueModule.java:137)
	at com.couchbase.client.java.json.RepackagedJsonValueModule.access$000(RepackagedJsonValueModule.java:33)
	at com.couchbase.client.java.json.RepackagedJsonValueModule$AbstractJsonValueDeserializer.decodeObject(RepackagedJsonValueModule.java:67)
	at com.couchbase.client.java.json.RepackagedJsonValueModule$JsonObjectDeserializer.deserialize(RepackagedJsonValueModule.java:131)
	at com.couchbase.client.java.json.RepackagedJsonValueModule$JsonObjectDeserializer.deserialize(RepackagedJsonValueModule.java:128)
	at com.couchbase.client.core.deps.com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4524)
	at com.couchbase.client.core.deps.com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3527)
	at com.couchbase.client.java.codec.DefaultJsonSerializer.deserialize(DefaultJsonSerializer.java:87)
	... 3 more

Can someone tell me what I am doing wrong or how should this be done?

Hi Manzoor,

specs.add(LookupInSpec.get(“name”));

The value of the “name” field is a string. It’s not possible to get the content as a JsonObject, because it’s not a JSON Object :slight_smile:

There are a couple of ways you could fix this. If you know the value is a string, read it like this:

String value = lookupResult.contentAs(0, String.class);

If you don’t know the type in advance, you can parse it as a Jackson JsonNode. This class can represent any kind of JSON value:

JsonNode value = lookupResult.contentAs(0, JsonNode.class);

It looks like you’re using the version of Jackson that’s repackaged inside the Couchbase SDK, so the class to import would be com.couchbase.client.core.deps.com.fasterxml.jackson.databind.JsonNode

Another option, if you don’t know the type in advance, would be to write:

Object value = lookupResult.contentAs(0, Object.class);

That would return the value as a String, List, Map, boxed primitive, or null.

Thanks,
David

Thank you David,

I have another Query:

I have a pojo Employee with four fields (String name, int age, String email ,int phone)
The Actual document in couchbase contains around 20 fields. But I need to select only the above four fields mentioned in pojo.

Approach 1:
I cannot use a kv api like get , because it will return the entire object.
Approach 2
I could have written the below query to apply projection.
select name,age,email,phone from emp_bucket use keys “sample”
But it involves the query service and data service, since I know the document key , I am planning to use sub document API which will involve only the data service.
Approach 3
Use subdocument API lookupIn to get the fields and build the pojo using reflection.
code
public static void main(String args) {
Cluster cluster = Cluster.connect(host, userName, password);
Bucket bucket = cluster.bucket(bucketName);
Collection empCollection = bucket.defaultCollection();
String documentId = “sample123”;

		Employee employee = new Employee();

		List<LookupInSpec> specs = new ArrayList<>();
		for (Field field : employee.getClass().getDeclaredFields()) {
			specs.add(LookupInSpec.get(field.getName()));
		}

		LookupInResult lookupResult = empCollection.lookupIn(documentId, specs);

		for (int i = 0; i < 4; i++) {
			JsonNode value = lookupResult.contentAs(i, JsonNode.class);
			System.out.println("value : " + value);
		}
	}

Result:

value : “Employee1”
value : 20
value : “user@demo.com
value : 9823625211

This code is giving me the result I need, but the problem is when mapping the entity, if I can use the path attribute present in SubDocumentField . I can use it to map to the entity.
Is there a way to do this?
or is there a better approach to do this ?

Update: nevermind! I see the question was already answered by @david.nault. I think Discourse was being weird for me…

Hi @Manzoor128
lookupResult.contentAsObject(0) will try to convert field 0 into a JsonObject, but it’s actually a string. You need to instead do:

String name = lookupResult.contentAs(0, String.class);

Looks like this discussion was continued in a separate thread: Use Sub document API LookupIN to map multile fields to pojo using reflection

Yes I got the solution. Thanks for all your help.