DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Combining gRPC With Guice
  • Chain of Responsibility In Microservices
  • NullPointerException in Java: Causes and Ways to Avoid It
  • From Naked Objects to Naked Functions

Trending

  • The Promise of Personal Data for Better Living
  • Getting Started With Postgres: Three Free and Easy Ways
  • Cutting Big Data Costs: Effective Data Processing With Apache Spark
  • Streamlined Infrastructure Deployment: Harnessing the Power of Terraform and Feature Toggles
  1. DZone
  2. Coding
  3. Frameworks
  4. Implementing PATCH Verbs With Gson, JAXB, and Spark Framework

Implementing PATCH Verbs With Gson, JAXB, and Spark Framework

HttpURLConnection does not support PATCH, but sometimes it's helpful to use the PATCH verb. So how do we get around this? Read on for some options.

Alan Richardson user avatar by
Alan Richardson
·
Jul. 08, 17 · Tutorial
Like (3)
Save
Tweet
Share
8.61K Views

Join the DZone community and get the full member experience.

Join For Free

i had not planned to implement patch in my testing web service - at all, ever…

but mark turner’s comment on my previous blog post made me reconsider and now i’ll try and aim for as many http verbs as i can. mark asked: "we did a similar thing with the jersey client, but found one flaw: it couldn’t handle patch requests which are useful when testing. does gson handle this?"

the short answer is, gson can be used to handle this, and i present two ways of handling it below.

when implementing patch, because of the architecture i’m using, i have to solve 4 problems:

  • how to patch a pojo (domain)
  • how to parse a json or xml request in a patch request suitable to allow me to patch a pojo (api)
  • how to route a patch request (http rest)
  • how to send a patch request (test)

generic solution

i thought if i could find a generic solution then i should try that first as i can make fast progress that way.

how to patch a pojo (domain object)

for a generic solution, the first thing that popped into my mind was to use reflection to do this, and since my objects are currently pretty simple, that seems reasonable, given that my application is pretty low risk since it is to use for training or practice in how to test a rest api.

i thought i’d create a generic patcher that, given a hashmap, will use reflection to iterate over fields until it finds the field and then sets the value.

at the moment, all my fields are string, so that’s pretty simple to do.

@test
public void canpatchalisticator(){

    listicatorlist list = new listicatorlist("first title", "first desc");

    map<string,string> patches = new hashmap<string, string>();
    patches.put("title", "this is the new title");
    patches.put("createddate", "1996-04-01-14-54-23");
    patches.put("description", "new description");


    reflectionpatcher patcher = new reflectionpatcher(list, listicatorlist.class);

    patcher.patch(patches);

    assert.assertequals("this is the new title", list.gettitle());
    assert.assertequals("1996-04-01-14-54-23", list.getcreateddate());
    assert.assertequals("new description", list.getdescription());

    assert.assertequals(0, patcher.getfieldsinerror().size());
    assert.assertequals(3, patcher.getfieldspatched().size());

}

and the reflectionpatcher is also pretty simple:

public class reflectionpatcher {

    private final object thing;
    private final class theclass;

    private list<string> fieldsinerror = new arraylist<>();
    private list<string> fieldspatched = new arraylist<>();

    public reflectionpatcher(object thing, class theclass) {
        this.thing = thing;
        this.theclass = theclass;

    }

    public void patch(map<string, string> patches) {
        for(string fieldname : patches.keyset()){
            boolean hadtosetaccessible = false;
            field declaration=null;
            field field=null;

            try {
                declaration = theclass.getdeclaredfield(fieldname);
                if(!declaration.isaccessible()){
                    hadtosetaccessible = true;
                    declaration.setaccessible(true);
                }
                declaration.set(thing, patches.get(fieldname));
                fieldspatched.add(fieldname);

            } catch (nosuchfieldexception e) {
                e.printstacktrace();
                fieldsinerror.add(fieldname + " - did not exist" );
            } catch (illegalaccessexception e) {
                e.printstacktrace();
                fieldsinerror.add(fieldname + " - could not access");
            }finally {
                if(hadtosetaccessible=true && declaration!=null){
                    declaration.setaccessible(false);
                }
            }
        }
    }

    public list<string> getfieldsinerror() {
        return fieldsinerror;
    }

    public list<string> getfieldspatched() {
        return fieldspatched;
    }
}

not the prettiest, not the most robust, but for my current needs, this would work, and if i stick to simple objects with string fields, no nested elements, i’ve pretty much got patching of pojos sorted.

how to parse a json or xml request in a patch request

json

my patch requests would look something like this in json:

patch /lists/sdfjwer-siwejr-2342sn
{"title":"title4","author":"author2"}
  • the guid in the uri path
  • the part json in the body

and converting that to a hash with gson is simple, and is often how i use gson for tactical work when parsing.

return gson.fromjson(body, map.class);
  • turn the stringbody into a map.

xml

my xml looks a little different since outer tags have to be named, not just ‘an object.’ i could make it a <patch/> element, which would keep it consistent, but since this is rest, the verb patch pretty much tells the server what it needs, so i should be able to patch a list with:

patch /lists/sdfjwer-siwejr-2342sn
<list><title>title4</title><author>author2</author></list>

jaxb is good for serializing to an object, but doesn’t want to work with a map, so i turned to json-java from sean leary for help. again, another package i’ve used for tactical automating in the past.

this allowed me to…

map withhead = gson.fromjson(
                xml.tojsonobject(body).tostring(),
                   map.class);

create some json from the xml and then use gson to convert it to a map.

only one issue is that because of the extra ‘head’ <list> i have a nested map, and all i want are the fields and values, so i quickly thought:

arraylist outer = new arraylist();
outer.addall(withhead.keyset());
return (map<string, string>) withhead.get(outer.get(0));
  • get the key of the parent and then return the submap with all its children.

a more elegant solution for this will occur to me as soon as i hit publish on this post, or i’ll learn it from the helpful comments. i will investigate a more robust solution to this, but the reflection approach to the pojo amendment just needs a hashmap, so i’m done.

how to route a patch request

this simply required me wiring up the spark routing to the api method i created, which used the reflectionpatcher and the generic payload to map convertor.

patch("/lists/*", (request, response) -> {
    return api.patchlist(new sparkapirequest(request),
                         new sparkapiresponse(response)).getbody();
});

patch("/lists", (request, response) -> {
    response.status(405);
    return "";
});

how to send a patch request

i thought this was going to be easy:

con.setrequestmethod("patch");

set the request method on my httpurlconnection

but no. httpurlconnection does not support patch.

fortunately, spark supports “x-http-method-override.” and therefore if i send a post, with a header of:

x-http-method-override: patch

spark will treat the request as a patch and route it accordingly.

a "better" non-generic way

for my purposes, i can speed ahead with a non-generic way, but it would probably be better for me to have a more object-based approach. so i tried an experiment…

in my current code i have

  • domain objects: these have methods and logic and cool stuff but they are pojo with no annotations, etc.
  • payload objects: these are purely for serializing and deserializing (or marshaling and unmarshaling).

here is an example:

@xmlrootelement(name = "list")
public class listicatorlistpayload {
    public string guid;
    public string title;
    public string description;
    public string createddate;
    public string amendeddate;
}

so what would happen if i deserialized a partial json into that?

return gson.fromjson(body, listicatorlistpayload.class);

well, because all the fields are set to null at the start, if there is nothing in the json string, then they stay null, so i effectively have a ‘patch’ object where the patches are the non-null values.

what happens with xml?

jaxbcontext context = jaxbcontext.newinstance(listicatorlistpayload.class);
unmarshaller m = context.createunmarshaller();
return (listicatorlistpayload)m.unmarshal(new stringreader(body));

same result, a listicatorlistpayload where only the patched fields are non-null.

i could now create a method on my listicatorlist which takes a listicatorlistpayload as parameter and sets the fields which are non-null, a bit like a clone operation. or, and i suspect i would do this, create a listicatorlistpatcher which knows how to patch a domain object from a payload - i don’t really like the idea of my domain objects knowing anything about the api, but i’m happy for my api to know about the domain objects.

this seems like a more robust approach going forward, and if i introduce more complexity into my code base for object handling, then i’ll probably use that approach.

Patch (computing) Gson Requests Object (computer science) Framework

Published at DZone with permission of Alan Richardson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Combining gRPC With Guice
  • Chain of Responsibility In Microservices
  • NullPointerException in Java: Causes and Ways to Avoid It
  • From Naked Objects to Naked Functions

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: